File: /usr/src/linux/fs/nfsd/nfscache.c

1     /*
2      * linux/fs/nfsd/nfscache.c
3      *
4      * Request reply cache. This is currently a global cache, but this may
5      * change in the future and be a per-client cache.
6      *
7      * This code is heavily inspired by the 44BSD implementation, although
8      * it does things a bit differently.
9      *
10      * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
11      */
12     
13     #include <linux/kernel.h>
14     #include <linux/sched.h>
15     #include <linux/slab.h>
16     #include <linux/string.h>
17     
18     #include <linux/sunrpc/svc.h>
19     #include <linux/nfsd/nfsd.h>
20     #include <linux/nfsd/cache.h>
21     
22     /* Size of reply cache. Common values are:
23      * 4.3BSD:	128
24      * 4.4BSD:	256
25      * Solaris2:	1024
26      * DEC Unix:	512-4096
27      */
28     #define CACHESIZE		1024
29     #define HASHSIZE		64
30     #define REQHASH(xid)		((((xid) >> 24) ^ (xid)) & (HASHSIZE-1))
31     
32     struct nfscache_head {
33     	struct svc_cacherep *	next;
34     	struct svc_cacherep *	prev;
35     };
36     
37     static struct nfscache_head *	hash_list;
38     static struct svc_cacherep *	lru_head;
39     static struct svc_cacherep *	lru_tail;
40     static struct svc_cacherep *	nfscache;
41     static int			cache_initialized;
42     static int			cache_disabled = 1;
43     
44     static int	nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data);
45     
46     void
47     nfsd_cache_init(void)
48     {
49     	struct svc_cacherep	*rp;
50     	struct nfscache_head	*rh;
51     	size_t			i;
52     	unsigned long		order;
53     
54     	if (cache_initialized)
55     		return;
56     
57     	i = CACHESIZE * sizeof (struct svc_cacherep);
58     	for (order = 0; (PAGE_SIZE << order) < i; order++)
59     		;
60     	nfscache = (struct svc_cacherep *)
61     		__get_free_pages(GFP_KERNEL, order);
62     	if (!nfscache) {
63     		printk (KERN_ERR "nfsd: cannot allocate %Zd bytes for reply cache\n", i);
64     		return;
65     	}
66     	memset(nfscache, 0, i);
67     
68     	i = HASHSIZE * sizeof (struct nfscache_head);
69     	hash_list = kmalloc (i, GFP_KERNEL);
70     	if (!hash_list) {
71     		free_pages ((unsigned long)nfscache, order);
72     		nfscache = NULL;
73     		printk (KERN_ERR "nfsd: cannot allocate %Zd bytes for hash list\n", i);
74     		return;
75     	}
76     
77     	for (i = 0, rh = hash_list; i < HASHSIZE; i++, rh++)
78     		rh->next = rh->prev = (struct svc_cacherep *) rh;
79     
80     	for (i = 0, rp = nfscache; i < CACHESIZE; i++, rp++) {
81     		rp->c_state = RC_UNUSED;
82     		rp->c_type = RC_NOCACHE;
83     		rp->c_hash_next =
84     		rp->c_hash_prev = rp;
85     		rp->c_lru_next = rp + 1;
86     		rp->c_lru_prev = rp - 1;
87     	}
88     	lru_head = nfscache;
89     	lru_tail = nfscache + CACHESIZE - 1;
90     	lru_head->c_lru_prev = NULL;
91     	lru_tail->c_lru_next = NULL;
92     
93     	cache_initialized = 1;
94     	cache_disabled = 0;
95     }
96     
97     void
98     nfsd_cache_shutdown(void)
99     {
100     	struct svc_cacherep	*rp;
101     	size_t			i;
102     	unsigned long		order;
103     
104     	if (!cache_initialized)
105     		return;
106     
107     	for (rp = lru_head; rp; rp = rp->c_lru_next) {
108     		if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF)
109     			kfree(rp->c_replbuf.buf);
110     	}
111     
112     	cache_initialized = 0;
113     	cache_disabled = 1;
114     
115     	i = CACHESIZE * sizeof (struct svc_cacherep);
116     	for (order = 0; (PAGE_SIZE << order) < i; order++)
117     		;
118     	free_pages ((unsigned long)nfscache, order);
119     	nfscache = NULL;
120     	kfree (hash_list);
121     	hash_list = NULL;
122     }
123     
124     /*
125      * Move cache entry to front of LRU list
126      */
127     static void
128     lru_put_front(struct svc_cacherep *rp)
129     {
130     	struct svc_cacherep	*prev = rp->c_lru_prev,
131     				*next = rp->c_lru_next;
132     
133     	if (prev)
134     		prev->c_lru_next = next;
135     	else
136     		lru_head = next;
137     	if (next)
138     		next->c_lru_prev = prev;
139     	else
140     		lru_tail = prev;
141     
142     	rp->c_lru_next = lru_head;
143     	rp->c_lru_prev = NULL;
144     	if (lru_head)
145     		lru_head->c_lru_prev = rp;
146     	lru_head = rp;
147     }
148     
149     /*
150      * Move a cache entry from one hash list to another
151      */
152     static void
153     hash_refile(struct svc_cacherep *rp)
154     {
155     	struct svc_cacherep	*prev = rp->c_hash_prev,
156     				*next = rp->c_hash_next;
157     	struct nfscache_head	*head = hash_list + REQHASH(rp->c_xid);
158     
159     	prev->c_hash_next = next;
160     	next->c_hash_prev = prev;
161     
162     	rp->c_hash_next = head->next;
163     	rp->c_hash_prev = (struct svc_cacherep *) head;
164     	head->next->c_hash_prev = rp;
165     	head->next = rp;
166     }
167     
168     /*
169      * Try to find an entry matching the current call in the cache. When none
170      * is found, we grab the oldest unlocked entry off the LRU list.
171      * Note that no operation within the loop may sleep.
172      */
173     int
174     nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
175     {
176     	struct svc_cacherep	*rh, *rp;
177     	u32			xid = rqstp->rq_xid,
178     				proto =  rqstp->rq_prot,
179     				vers = rqstp->rq_vers,
180     				proc = rqstp->rq_proc;
181     	unsigned long		age;
182     
183     	rqstp->rq_cacherep = NULL;
184     	if (cache_disabled || type == RC_NOCACHE) {
185     		nfsdstats.rcnocache++;
186     		return RC_DOIT;
187     	}
188     
189     	rp = rh = (struct svc_cacherep *) &hash_list[REQHASH(xid)];
190     	while ((rp = rp->c_hash_next) != rh) {
191     		if (rp->c_state != RC_UNUSED &&
192     		    xid == rp->c_xid && proc == rp->c_proc &&
193     		    proto == rp->c_prot && vers == rp->c_vers &&
194     		    time_before(jiffies, rp->c_timestamp + 120*HZ) &&
195     		    memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, sizeof(rp->c_addr))==0) {
196     			nfsdstats.rchits++;
197     			goto found_entry;
198     		}
199     	}
200     	nfsdstats.rcmisses++;
201     
202     	/* This loop shouldn't take more than a few iterations normally */
203     	{
204     	int	safe = 0;
205     	for (rp = lru_tail; rp; rp = rp->c_lru_prev) {
206     		if (rp->c_state != RC_INPROG)
207     			break;
208     		if (safe++ > CACHESIZE) {
209     			printk("nfsd: loop in repcache LRU list\n");
210     			cache_disabled = 1;
211     			return RC_DOIT;
212     		}
213     	}
214     	}
215     
216     	/* This should not happen */
217     	if (rp == NULL) {
218     		static int	complaints;
219     
220     		printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
221     		if (++complaints > 5) {
222     			printk(KERN_WARNING "nfsd: disabling repcache.\n");
223     			cache_disabled = 1;
224     		}
225     		return RC_DOIT;
226     	}
227     
228     	rqstp->rq_cacherep = rp;
229     	rp->c_state = RC_INPROG;
230     	rp->c_xid = xid;
231     	rp->c_proc = proc;
232     	rp->c_addr = rqstp->rq_addr;
233     	rp->c_prot = proto;
234     	rp->c_vers = vers;
235     	rp->c_timestamp = jiffies;
236     
237     	hash_refile(rp);
238     
239     	/* release any buffer */
240     	if (rp->c_type == RC_REPLBUFF) {
241     		kfree(rp->c_replbuf.buf);
242     		rp->c_replbuf.buf = NULL;
243     	}
244     	rp->c_type = RC_NOCACHE;
245     
246     	return RC_DOIT;
247     
248     found_entry:
249     	/* We found a matching entry which is either in progress or done. */
250     	age = jiffies - rp->c_timestamp;
251     	rp->c_timestamp = jiffies;
252     	lru_put_front(rp);
253     
254     	/* Request being processed or excessive rexmits */
255     	if (rp->c_state == RC_INPROG || age < RC_DELAY)
256     		return RC_DROPIT;
257     
258     	/* From the hall of fame of impractical attacks:
259     	 * Is this a user who tries to snoop on the cache? */
260     	if (!rqstp->rq_secure && rp->c_secure)
261     		return RC_DOIT;
262     
263     	/* Compose RPC reply header */
264     	switch (rp->c_type) {
265     	case RC_NOCACHE:
266     		return RC_DOIT;
267     	case RC_REPLSTAT:
268     		svc_putlong(&rqstp->rq_resbuf, rp->c_replstat);
269     		break;
270     	case RC_REPLBUFF:
271     		if (!nfsd_cache_append(rqstp, &rp->c_replbuf))
272     			return RC_DOIT;	/* should not happen */
273     		break;
274     	default:
275     		printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type);
276     		rp->c_state = RC_UNUSED;
277     		return RC_DOIT;
278     	}
279     
280     	return RC_REPLY;
281     }
282     
283     /*
284      * Update a cache entry. This is called from nfsd_dispatch when
285      * the procedure has been executed and the complete reply is in
286      * rqstp->rq_res.
287      *
288      * We're copying around data here rather than swapping buffers because
289      * the toplevel loop requires max-sized buffers, which would be a waste
290      * of memory for a cache with a max reply size of 100 bytes (diropokres).
291      *
292      * If we should start to use different types of cache entries tailored
293      * specifically for attrstat and fh's, we may save even more space.
294      *
295      * Also note that a cachetype of RC_NOCACHE can legally be passed when
296      * nfsd failed to encode a reply that otherwise would have been cached.
297      * In this case, nfsd_cache_update is called with statp == NULL.
298      */
299     void
300     nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp)
301     {
302     	struct svc_cacherep *rp;
303     	struct svc_buf	*resp = &rqstp->rq_resbuf, *cachp;
304     	int		len;
305     
306     	if (!(rp = rqstp->rq_cacherep) || cache_disabled)
307     		return;
308     
309     	len = resp->len - (statp - resp->base);
310     	
311     	/* Don't cache excessive amounts of data and XDR failures */
312     	if (!statp || len > (256 >> 2)) {
313     		rp->c_state = RC_UNUSED;
314     		return;
315     	}
316     
317     	switch (cachetype) {
318     	case RC_REPLSTAT:
319     		if (len != 1)
320     			printk("nfsd: RC_REPLSTAT/reply len %d!\n",len);
321     		rp->c_replstat = *statp;
322     		break;
323     	case RC_REPLBUFF:
324     		cachp = &rp->c_replbuf;
325     		cachp->buf = (u32 *) kmalloc(len << 2, GFP_KERNEL);
326     		if (!cachp->buf) {
327     			rp->c_state = RC_UNUSED;
328     			return;
329     		}
330     		cachp->len = len;
331     		memcpy(cachp->buf, statp, len << 2);
332     		break;
333     	}
334     
335     	lru_put_front(rp);
336     	rp->c_secure = rqstp->rq_secure;
337     	rp->c_type = cachetype;
338     	rp->c_state = RC_DONE;
339     	rp->c_timestamp = jiffies;
340     
341     	return;
342     }
343     
344     /*
345      * Copy cached reply to current reply buffer. Should always fit.
346      */
347     static int
348     nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data)
349     {
350     	struct svc_buf	*resp = &rqstp->rq_resbuf;
351     
352     	if (resp->len + data->len > resp->buflen) {
353     		printk(KERN_WARNING "nfsd: cached reply too large (%d).\n",
354     				data->len);
355     		return 0;
356     	}
357     	memcpy(resp->buf, data->buf, data->len << 2);
358     	resp->buf += data->len;
359     	resp->len += data->len;
360     	return 1;
361     }
362