Blame view

fs/nfsd/nfscache.c 15.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
   * 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>
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/slab.h>
5976687a2   Jeff Layton   sunrpc: move addr...
11
  #include <linux/sunrpc/addr.h>
0338dd157   Jeff Layton   nfsd: dynamically...
12
  #include <linux/highmem.h>
0733c7ba1   Jeff Layton   nfsd: scale up th...
13
14
  #include <linux/log2.h>
  #include <linux/hash.h>
01a7decf7   Jeff Layton   nfsd: keep a chec...
15
  #include <net/checksum.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16

9a74af213   Boaz Harrosh   nfsd: Move privat...
17
18
  #include "nfsd.h"
  #include "cache.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

0338dd157   Jeff Layton   nfsd: dynamically...
20
  #define NFSDDBG_FACILITY	NFSDDBG_REPCACHE
0733c7ba1   Jeff Layton   nfsd: scale up th...
21
22
23
24
25
26
  /*
   * We use this value to determine the number of hash buckets from the max
   * cache size, the idea being that when the cache is at its maximum number
   * of entries, then this should be the average number of entries per bucket.
   */
  #define TARGET_BUCKET_SIZE	64
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

7142b98d9   Trond Myklebust   nfsd: Clean up dr...
28
  struct nfsd_drc_bucket {
bedd4b61a   Trond Myklebust   nfsd: convert the...
29
  	struct list_head lru_head;
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
30
  	spinlock_t cache_lock;
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
31
32
33
  };
  
  static struct nfsd_drc_bucket	*drc_hashtbl;
8a8bc40d9   Jeff Layton   nfsd: create a de...
34
  static struct kmem_cache	*drc_slab;
9dc56143c   Jeff Layton   nfsd: break out c...
35
36
  
  /* max number of entries allowed in the cache */
0338dd157   Jeff Layton   nfsd: dynamically...
37
  static unsigned int		max_drc_entries;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

0733c7ba1   Jeff Layton   nfsd: scale up th...
39
40
  /* number of significant bits in the hash value */
  static unsigned int		maskbits;
bedd4b61a   Trond Myklebust   nfsd: convert the...
41
  static unsigned int		drc_hashsize;
0733c7ba1   Jeff Layton   nfsd: scale up th...
42

fca4217c5   Greg Banks   knfsd: reply cach...
43
  /*
9dc56143c   Jeff Layton   nfsd: break out c...
44
45
46
47
48
   * 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 */
31e60f522   Trond Myklebust   nfsd: convert num...
49
  static atomic_t			num_drc_entries;
9dc56143c   Jeff Layton   nfsd: break out c...
50
51
52
  
  /* cache misses due only to checksum comparison failures */
  static unsigned int		payload_misses;
6c6910cd4   Jeff Layton   nfsd: track memor...
53
54
  /* amount of memory (in bytes) currently consumed by the DRC */
  static unsigned int		drc_mem_usage;
98d821bda   Jeff Layton   nfsd: keep stats ...
55
56
57
58
59
  /* 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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  static int	nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
1ab6c4997   Dave Chinner   fs: convert fs sh...
61
62
63
64
  static unsigned long nfsd_reply_cache_count(struct shrinker *shrink,
  					    struct shrink_control *sc);
  static unsigned long nfsd_reply_cache_scan(struct shrinker *shrink,
  					   struct shrink_control *sc);
b4e7f2c94   Jeff Layton   nfsd: register a ...
65

c8c797f9f   Wei Yongjun   nfsd: make symbol...
66
  static struct shrinker nfsd_reply_cache_shrinker = {
1ab6c4997   Dave Chinner   fs: convert fs sh...
67
68
  	.scan_objects = nfsd_reply_cache_scan,
  	.count_objects = nfsd_reply_cache_count,
b4e7f2c94   Jeff Layton   nfsd: register a ...
69
70
  	.seeks	= 1,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

fca4217c5   Greg Banks   knfsd: reply cach...
72
  /*
0338dd157   Jeff Layton   nfsd: dynamically...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
   * 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);
  }
0733c7ba1   Jeff Layton   nfsd: scale up th...
99
100
101
102
103
104
105
106
107
  /*
   * Compute the number of hash buckets we need. Divide the max cachesize by
   * the "target" max bucket size, and round up to next power of two.
   */
  static unsigned int
  nfsd_hashsize(unsigned int limit)
  {
  	return roundup_pow_of_two(limit / TARGET_BUCKET_SIZE);
  }
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
108
109
110
111
112
  static u32
  nfsd_cache_hash(__be32 xid)
  {
  	return hash_32(be32_to_cpu(xid), maskbits);
  }
f09841fdf   Jeff Layton   nfsd: add alloc a...
113
114
  static struct svc_cacherep *
  nfsd_reply_cache_alloc(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  {
  	struct svc_cacherep	*rp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

f09841fdf   Jeff Layton   nfsd: add alloc a...
118
119
  	rp = kmem_cache_alloc(drc_slab, GFP_KERNEL);
  	if (rp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  		rp->c_state = RC_UNUSED;
  		rp->c_type = RC_NOCACHE;
f09841fdf   Jeff Layton   nfsd: add alloc a...
122
  		INIT_LIST_HEAD(&rp->c_lru);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	}
f09841fdf   Jeff Layton   nfsd: add alloc a...
124
125
  	return rp;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126

f09841fdf   Jeff Layton   nfsd: add alloc a...
127
128
129
  static void
  nfsd_reply_cache_free_locked(struct svc_cacherep *rp)
  {
6c6910cd4   Jeff Layton   nfsd: track memor...
130
131
  	if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) {
  		drc_mem_usage -= rp->c_replvec.iov_len;
f09841fdf   Jeff Layton   nfsd: add alloc a...
132
  		kfree(rp->c_replvec.iov_base);
6c6910cd4   Jeff Layton   nfsd: track memor...
133
  	}
f09841fdf   Jeff Layton   nfsd: add alloc a...
134
  	list_del(&rp->c_lru);
31e60f522   Trond Myklebust   nfsd: convert num...
135
  	atomic_dec(&num_drc_entries);
6c6910cd4   Jeff Layton   nfsd: track memor...
136
  	drc_mem_usage -= sizeof(*rp);
f09841fdf   Jeff Layton   nfsd: add alloc a...
137
138
  	kmem_cache_free(drc_slab, rp);
  }
2c6b691c0   Jeff Layton   nfsd: when updati...
139
  static void
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
140
  nfsd_reply_cache_free(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
2c6b691c0   Jeff Layton   nfsd: when updati...
141
  {
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
142
  	spin_lock(&b->cache_lock);
2c6b691c0   Jeff Layton   nfsd: when updati...
143
  	nfsd_reply_cache_free_locked(rp);
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
144
  	spin_unlock(&b->cache_lock);
2c6b691c0   Jeff Layton   nfsd: when updati...
145
  }
f09841fdf   Jeff Layton   nfsd: add alloc a...
146
147
  int nfsd_reply_cache_init(void)
  {
0733c7ba1   Jeff Layton   nfsd: scale up th...
148
  	unsigned int hashsize;
bedd4b61a   Trond Myklebust   nfsd: convert the...
149
  	unsigned int i;
a68465c9c   Kinglong Mee   NFSD: Error out w...
150
  	int status = 0;
0733c7ba1   Jeff Layton   nfsd: scale up th...
151

ac534ff2d   Jeff Layton   nfsd: fix startup...
152
  	max_drc_entries = nfsd_cache_size_limit();
31e60f522   Trond Myklebust   nfsd: convert num...
153
  	atomic_set(&num_drc_entries, 0);
0733c7ba1   Jeff Layton   nfsd: scale up th...
154
155
  	hashsize = nfsd_hashsize(max_drc_entries);
  	maskbits = ilog2(hashsize);
ac534ff2d   Jeff Layton   nfsd: fix startup...
156

a68465c9c   Kinglong Mee   NFSD: Error out w...
157
158
159
  	status = register_shrinker(&nfsd_reply_cache_shrinker);
  	if (status)
  		return status;
8a8bc40d9   Jeff Layton   nfsd: create a de...
160
161
162
163
  	drc_slab = kmem_cache_create("nfsd_drc", sizeof(struct svc_cacherep),
  					0, 0, NULL);
  	if (!drc_slab)
  		goto out_nomem;
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
164
165
  	drc_hashtbl = kcalloc(hashsize, sizeof(*drc_hashtbl), GFP_KERNEL);
  	if (!drc_hashtbl)
d5c3428b2   J. Bruce Fields   nfsd: fail module...
166
  		goto out_nomem;
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
167
  	for (i = 0; i < hashsize; i++) {
bedd4b61a   Trond Myklebust   nfsd: convert the...
168
  		INIT_LIST_HEAD(&drc_hashtbl[i].lru_head);
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
169
170
  		spin_lock_init(&drc_hashtbl[i].cache_lock);
  	}
bedd4b61a   Trond Myklebust   nfsd: convert the...
171
  	drc_hashsize = hashsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172

d5c3428b2   J. Bruce Fields   nfsd: fail module...
173
174
175
176
177
178
  	return 0;
  out_nomem:
  	printk(KERN_ERR "nfsd: failed to allocate reply cache
  ");
  	nfsd_reply_cache_shutdown();
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  }
d5c3428b2   J. Bruce Fields   nfsd: fail module...
180
  void nfsd_reply_cache_shutdown(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  {
  	struct svc_cacherep	*rp;
bedd4b61a   Trond Myklebust   nfsd: convert the...
183
  	unsigned int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184

b4e7f2c94   Jeff Layton   nfsd: register a ...
185
  	unregister_shrinker(&nfsd_reply_cache_shrinker);
aca8a23de   Jeff Layton   nfsd: add recurri...
186

bedd4b61a   Trond Myklebust   nfsd: convert the...
187
188
189
190
191
192
  	for (i = 0; i < drc_hashsize; i++) {
  		struct list_head *head = &drc_hashtbl[i].lru_head;
  		while (!list_empty(head)) {
  			rp = list_first_entry(head, struct svc_cacherep, c_lru);
  			nfsd_reply_cache_free_locked(rp);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  	}
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
194
195
  	kfree (drc_hashtbl);
  	drc_hashtbl = NULL;
bedd4b61a   Trond Myklebust   nfsd: convert the...
196
  	drc_hashsize = 0;
8a8bc40d9   Jeff Layton   nfsd: create a de...
197

e79017ddc   Julia Lawall   nfsd: drop null t...
198
199
  	kmem_cache_destroy(drc_slab);
  	drc_slab = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
  }
  
  /*
aca8a23de   Jeff Layton   nfsd: add recurri...
203
204
   * Move cache entry to end of LRU list, and queue the cleaner to run if it's
   * not already scheduled.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
   */
  static void
bedd4b61a   Trond Myklebust   nfsd: convert the...
207
  lru_put_end(struct nfsd_drc_bucket *b, struct svc_cacherep *rp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  {
56c2548b2   Jeff Layton   nfsd: always move...
209
  	rp->c_timestamp = jiffies;
bedd4b61a   Trond Myklebust   nfsd: convert the...
210
  	list_move_tail(&rp->c_lru, &b->lru_head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  }
1ab6c4997   Dave Chinner   fs: convert fs sh...
212
  static long
bedd4b61a   Trond Myklebust   nfsd: convert the...
213
  prune_bucket(struct nfsd_drc_bucket *b)
aca8a23de   Jeff Layton   nfsd: add recurri...
214
215
  {
  	struct svc_cacherep *rp, *tmp;
1ab6c4997   Dave Chinner   fs: convert fs sh...
216
  	long freed = 0;
aca8a23de   Jeff Layton   nfsd: add recurri...
217

bedd4b61a   Trond Myklebust   nfsd: convert the...
218
  	list_for_each_entry_safe(rp, tmp, &b->lru_head, c_lru) {
1b19453d1   Jeff Layton   nfsd: don't halt ...
219
220
221
222
223
224
  		/*
  		 * Don't free entries attached to calls that are still
  		 * in-progress, but do keep scanning the list.
  		 */
  		if (rp->c_state == RC_INPROG)
  			continue;
31e60f522   Trond Myklebust   nfsd: convert num...
225
  		if (atomic_read(&num_drc_entries) <= max_drc_entries &&
1b19453d1   Jeff Layton   nfsd: don't halt ...
226
  		    time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
aca8a23de   Jeff Layton   nfsd: add recurri...
227
228
  			break;
  		nfsd_reply_cache_free_locked(rp);
1ab6c4997   Dave Chinner   fs: convert fs sh...
229
  		freed++;
aca8a23de   Jeff Layton   nfsd: add recurri...
230
  	}
bedd4b61a   Trond Myklebust   nfsd: convert the...
231
232
233
234
235
236
237
238
239
240
241
242
  	return freed;
  }
  
  /*
   * 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 long
  prune_cache_entries(void)
  {
  	unsigned int i;
  	long freed = 0;
bedd4b61a   Trond Myklebust   nfsd: convert the...
243
244
245
  
  	for (i = 0; i < drc_hashsize; i++) {
  		struct nfsd_drc_bucket *b = &drc_hashtbl[i];
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
246
247
248
  		if (list_empty(&b->lru_head))
  			continue;
  		spin_lock(&b->cache_lock);
bedd4b61a   Trond Myklebust   nfsd: convert the...
249
  		freed += prune_bucket(b);
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
250
  		spin_unlock(&b->cache_lock);
bedd4b61a   Trond Myklebust   nfsd: convert the...
251
  	}
1ab6c4997   Dave Chinner   fs: convert fs sh...
252
  	return freed;
aca8a23de   Jeff Layton   nfsd: add recurri...
253
  }
1ab6c4997   Dave Chinner   fs: convert fs sh...
254
255
  static unsigned long
  nfsd_reply_cache_count(struct shrinker *shrink, struct shrink_control *sc)
b4e7f2c94   Jeff Layton   nfsd: register a ...
256
  {
31e60f522   Trond Myklebust   nfsd: convert num...
257
  	return atomic_read(&num_drc_entries);
b4e7f2c94   Jeff Layton   nfsd: register a ...
258
  }
1ab6c4997   Dave Chinner   fs: convert fs sh...
259
260
261
  static unsigned long
  nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
  {
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
262
  	return prune_cache_entries();
1ab6c4997   Dave Chinner   fs: convert fs sh...
263
  }
aca8a23de   Jeff Layton   nfsd: add recurri...
264
  /*
01a7decf7   Jeff Layton   nfsd: keep a chec...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
   * 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;
56edc86b5   Jeff Layton   nfsd: fix compile...
288
  		len = min_t(size_t, PAGE_SIZE - base, csum_len);
01a7decf7   Jeff Layton   nfsd: keep a chec...
289
290
291
292
293
294
295
  		csum = csum_partial(p, len, csum);
  		csum_len -= len;
  		base = 0;
  		++idx;
  	}
  	return csum;
  }
9dc56143c   Jeff Layton   nfsd: break out c...
296
297
298
  static bool
  nfsd_cache_match(struct svc_rqst *rqstp, __wsum csum, struct svc_cacherep *rp)
  {
ef9b16dc6   Trond Myklebust   nfsd: Reorder nfs...
299
300
  	/* Check RPC XID first */
  	if (rqstp->rq_xid != rp->c_xid)
9dc56143c   Jeff Layton   nfsd: break out c...
301
  		return false;
9dc56143c   Jeff Layton   nfsd: break out c...
302
303
304
305
306
  	/* compare checksum of NFS data */
  	if (csum != rp->c_csum) {
  		++payload_misses;
  		return false;
  	}
ef9b16dc6   Trond Myklebust   nfsd: Reorder nfs...
307
308
309
310
311
312
313
314
  	/* Other discriminators */
  	if (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;
9dc56143c   Jeff Layton   nfsd: break out c...
315
316
  	return true;
  }
01a7decf7   Jeff Layton   nfsd: keep a chec...
317
  /*
a4a3ec329   Jeff Layton   nfsd: break out h...
318
319
320
321
322
   * 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 *
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
323
324
  nfsd_cache_search(struct nfsd_drc_bucket *b, struct svc_rqst *rqstp,
  		__wsum csum)
a4a3ec329   Jeff Layton   nfsd: break out h...
325
  {
98d821bda   Jeff Layton   nfsd: keep stats ...
326
  	struct svc_cacherep	*rp, *ret = NULL;
11acf6ef3   Trond Myklebust   nfsd: Remove the ...
327
  	struct list_head 	*rh = &b->lru_head;
98d821bda   Jeff Layton   nfsd: keep stats ...
328
  	unsigned int		entries = 0;
a4a3ec329   Jeff Layton   nfsd: break out h...
329

11acf6ef3   Trond Myklebust   nfsd: Remove the ...
330
  	list_for_each_entry(rp, rh, c_lru) {
98d821bda   Jeff Layton   nfsd: keep stats ...
331
332
333
334
335
336
337
338
339
340
  		++entries;
  		if (nfsd_cache_match(rqstp, csum, rp)) {
  			ret = rp;
  			break;
  		}
  	}
  
  	/* tally hash chain length stats */
  	if (entries > longest_chain) {
  		longest_chain = entries;
31e60f522   Trond Myklebust   nfsd: convert num...
341
  		longest_chain_cachesize = atomic_read(&num_drc_entries);
98d821bda   Jeff Layton   nfsd: keep stats ...
342
343
  	} else if (entries == longest_chain) {
  		/* prefer to keep the smallest cachesize possible here */
31e60f522   Trond Myklebust   nfsd: convert num...
344
345
346
  		longest_chain_cachesize = min_t(unsigned int,
  				longest_chain_cachesize,
  				atomic_read(&num_drc_entries));
a4a3ec329   Jeff Layton   nfsd: break out h...
347
  	}
98d821bda   Jeff Layton   nfsd: keep stats ...
348
349
  
  	return ret;
a4a3ec329   Jeff Layton   nfsd: break out h...
350
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
  /*
   * Try to find an entry matching the current call in the cache. When none
1ac836297   Jeff Layton   nfsd: fix comment...
353
354
355
356
   * 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.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
   */
  int
1091006c5   J. Bruce Fields   nfsd: turn on rep...
359
  nfsd_cache_lookup(struct svc_rqst *rqstp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  {
0338dd157   Jeff Layton   nfsd: dynamically...
361
  	struct svc_cacherep	*rp, *found;
c7afef1f9   Al Viro   [PATCH] nfsd: mis...
362
363
  	__be32			xid = rqstp->rq_xid;
  	u32			proto =  rqstp->rq_prot,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
  				vers = rqstp->rq_vers,
  				proc = rqstp->rq_proc;
01a7decf7   Jeff Layton   nfsd: keep a chec...
366
  	__wsum			csum;
7142b98d9   Trond Myklebust   nfsd: Clean up dr...
367
368
  	u32 hash = nfsd_cache_hash(xid);
  	struct nfsd_drc_bucket *b = &drc_hashtbl[hash];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  	unsigned long		age;
1091006c5   J. Bruce Fields   nfsd: turn on rep...
370
  	int type = rqstp->rq_cachetype;
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
371
  	int rtn = RC_DOIT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  
  	rqstp->rq_cacherep = NULL;
13cc8a78e   Jeff Layton   nfsd: remove the ...
374
  	if (type == RC_NOCACHE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  		nfsdstats.rcnocache++;
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
376
  		return rtn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  	}
01a7decf7   Jeff Layton   nfsd: keep a chec...
378
  	csum = nfsd_cache_csum(rqstp);
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
379
380
  	/*
  	 * Since the common case is a cache miss followed by an insert,
a0ef5e196   Jeff Layton   nfsd: don't try t...
381
  	 * preallocate an entry.
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
382
  	 */
0338dd157   Jeff Layton   nfsd: dynamically...
383
  	rp = nfsd_reply_cache_alloc();
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
384
  	spin_lock(&b->cache_lock);
6c6910cd4   Jeff Layton   nfsd: track memor...
385
  	if (likely(rp)) {
31e60f522   Trond Myklebust   nfsd: convert num...
386
  		atomic_inc(&num_drc_entries);
6c6910cd4   Jeff Layton   nfsd: track memor...
387
388
  		drc_mem_usage += sizeof(*rp);
  	}
0338dd157   Jeff Layton   nfsd: dynamically...
389

a0ef5e196   Jeff Layton   nfsd: don't try t...
390
  	/* go ahead and prune the cache */
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
391
  	prune_bucket(b);
a0ef5e196   Jeff Layton   nfsd: don't try t...
392

7142b98d9   Trond Myklebust   nfsd: Clean up dr...
393
  	found = nfsd_cache_search(b, rqstp, csum);
0338dd157   Jeff Layton   nfsd: dynamically...
394
  	if (found) {
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
395
396
  		if (likely(rp))
  			nfsd_reply_cache_free_locked(rp);
0338dd157   Jeff Layton   nfsd: dynamically...
397
398
  		rp = found;
  		goto found_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
  	}
0b9ea37f2   Jeff Layton   nfsd: eliminate o...
400
401
402
403
404
  	if (!rp) {
  		dprintk("nfsd: unable to allocate DRC entry!
  ");
  		goto out;
  	}
0338dd157   Jeff Layton   nfsd: dynamically...
405
  	nfsdstats.rcmisses++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
  	rqstp->rq_cacherep = rp;
  	rp->c_state = RC_INPROG;
  	rp->c_xid = xid;
  	rp->c_proc = proc;
7b9e8522a   Jeff Layton   nfsd: fix IPv6 ad...
410
411
  	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)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
  	rp->c_prot = proto;
  	rp->c_vers = vers;
01a7decf7   Jeff Layton   nfsd: keep a chec...
414
415
  	rp->c_len = rqstp->rq_arg.len;
  	rp->c_csum = csum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416

bedd4b61a   Trond Myklebust   nfsd: convert the...
417
  	lru_put_end(b, rp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
  
  	/* release any buffer */
  	if (rp->c_type == RC_REPLBUFF) {
6c6910cd4   Jeff Layton   nfsd: track memor...
421
  		drc_mem_usage -= rp->c_replvec.iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
426
  		kfree(rp->c_replvec.iov_base);
  		rp->c_replvec.iov_base = NULL;
  	}
  	rp->c_type = RC_NOCACHE;
   out:
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
427
  	spin_unlock(&b->cache_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
  	return rtn;
  
  found_entry:
0338dd157   Jeff Layton   nfsd: dynamically...
431
  	nfsdstats.rchits++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
  	/* We found a matching entry which is either in progress or done. */
  	age = jiffies - rp->c_timestamp;
bedd4b61a   Trond Myklebust   nfsd: convert the...
434
  	lru_put_end(b, rp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
441
442
443
  
  	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;
4d152e2c9   Jeff Layton   sunrpc: add a gen...
444
  	if (!test_bit(RQ_SECURE, &rqstp->rq_flags) && rp->c_secure)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
  		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
  ", rp->c_type);
0338dd157   Jeff Layton   nfsd: dynamically...
463
  		nfsd_reply_cache_free_locked(rp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  	}
  
  	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
c7afef1f9   Al Viro   [PATCH] nfsd: mis...
486
  nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  {
13cc8a78e   Jeff Layton   nfsd: remove the ...
488
  	struct svc_cacherep *rp = rqstp->rq_cacherep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
  	struct kvec	*resv = &rqstp->rq_res.head[0], *cachv;
bedd4b61a   Trond Myklebust   nfsd: convert the...
490
491
  	u32		hash;
  	struct nfsd_drc_bucket *b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
  	int		len;
6c6910cd4   Jeff Layton   nfsd: track memor...
493
  	size_t		bufsize = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

13cc8a78e   Jeff Layton   nfsd: remove the ...
495
  	if (!rp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  		return;
bedd4b61a   Trond Myklebust   nfsd: convert the...
497
498
  	hash = nfsd_cache_hash(rp->c_xid);
  	b = &drc_hashtbl[hash];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
  	len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
  	len >>= 2;
fca4217c5   Greg Banks   knfsd: reply cach...
501

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  	/* Don't cache excessive amounts of data and XDR failures */
  	if (!statp || len > (256 >> 2)) {
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
504
  		nfsd_reply_cache_free(b, rp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
509
510
511
512
513
514
515
516
  		return;
  	}
  
  	switch (cachetype) {
  	case RC_REPLSTAT:
  		if (len != 1)
  			printk("nfsd: RC_REPLSTAT/reply len %d!
  ",len);
  		rp->c_replstat = *statp;
  		break;
  	case RC_REPLBUFF:
  		cachv = &rp->c_replvec;
6c6910cd4   Jeff Layton   nfsd: track memor...
517
518
  		bufsize = len << 2;
  		cachv->iov_base = kmalloc(bufsize, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
  		if (!cachv->iov_base) {
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
520
  			nfsd_reply_cache_free(b, rp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
  			return;
  		}
6c6910cd4   Jeff Layton   nfsd: track memor...
523
524
  		cachv->iov_len = bufsize;
  		memcpy(cachv->iov_base, statp, bufsize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
  		break;
2c6b691c0   Jeff Layton   nfsd: when updati...
526
  	case RC_NOCACHE:
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
527
  		nfsd_reply_cache_free(b, rp);
2c6b691c0   Jeff Layton   nfsd: when updati...
528
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
  	}
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
530
  	spin_lock(&b->cache_lock);
6c6910cd4   Jeff Layton   nfsd: track memor...
531
  	drc_mem_usage += bufsize;
bedd4b61a   Trond Myklebust   nfsd: convert the...
532
  	lru_put_end(b, rp);
4d152e2c9   Jeff Layton   sunrpc: add a gen...
533
  	rp->c_secure = test_bit(RQ_SECURE, &rqstp->rq_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
  	rp->c_type = cachetype;
  	rp->c_state = RC_DONE;
89a26b3d2   Trond Myklebust   nfsd: split DRC g...
536
  	spin_unlock(&b->cache_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
  	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).
  ",
  				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;
  }
a2f999a37   Jeff Layton   nfsd: add new rep...
560
561
562
563
564
565
566
567
  
  /*
   * 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)
  {
a2f999a37   Jeff Layton   nfsd: add new rep...
568
569
  	seq_printf(m, "max entries:           %u
  ", max_drc_entries);
31e60f522   Trond Myklebust   nfsd: convert num...
570
571
572
  	seq_printf(m, "num entries:           %u
  ",
  			atomic_read(&num_drc_entries));
0733c7ba1   Jeff Layton   nfsd: scale up th...
573
574
  	seq_printf(m, "hash buckets:          %u
  ", 1 << maskbits);
a2f999a37   Jeff Layton   nfsd: add new rep...
575
576
577
578
579
580
581
582
583
584
  	seq_printf(m, "mem usage:             %u
  ", drc_mem_usage);
  	seq_printf(m, "cache hits:            %u
  ", nfsdstats.rchits);
  	seq_printf(m, "cache misses:          %u
  ", nfsdstats.rcmisses);
  	seq_printf(m, "not cached:            %u
  ", nfsdstats.rcnocache);
  	seq_printf(m, "payload misses:        %u
  ", payload_misses);
98d821bda   Jeff Layton   nfsd: keep stats ...
585
586
587
588
  	seq_printf(m, "longest chain len:     %u
  ", longest_chain);
  	seq_printf(m, "cachesize at longest:  %u
  ", longest_chain_cachesize);
a2f999a37   Jeff Layton   nfsd: add new rep...
589
590
591
592
593
594
595
  	return 0;
  }
  
  int nfsd_reply_cache_stats_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, nfsd_reply_cache_stats_show, NULL);
  }