Merge remote-tracking branch 'remotes/crater/vendor/BYACC'
[dragonfly.git] / sys / vfs / nfs / nfs_srvcache.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *      @(#)nfs_srvcache.c      8.3 (Berkeley) 3/30/95
33  * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $
34  */
35
36 /*
37  * Reference: Chet Juszczak, "Improving the Performance and Correctness
38  *              of an NFS Server", in Proc. Winter 1989 USENIX Conference,
39  *              pages 53-63. San Diego, February 1989.
40  */
41 #include <sys/param.h>
42 #include <sys/malloc.h>
43 #include <sys/mount.h>
44 #include <sys/systm.h>
45 #include <sys/mbuf.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>      /* for dup_sockaddr */
48
49 #include <netinet/in.h>
50 #include "rpcv2.h"
51 #include "nfsproto.h"
52 #include "nfs.h"
53 #include "nfsrvcache.h"
54
55 #ifndef NFS_NOSERVER 
56 static long numnfsrvcache;
57 static long desirednfsrvcache;
58
59 #define NFSRCHASH(xid) \
60         (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
61 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
62 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
63 static u_long nfsrvhash;
64
65 #define TRUE    1
66 #define FALSE   0
67
68 struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token);
69
70 /*
71  * Static array that defines which nfs rpc's are nonidempotent
72  */
73 static int nonidempotent[NFS_NPROCS] = {
74         FALSE,
75         FALSE,
76         TRUE,
77         FALSE,
78         FALSE,
79         FALSE,
80         FALSE,
81         TRUE,
82         TRUE,
83         TRUE,
84         TRUE,
85         TRUE,
86         TRUE,
87         TRUE,
88         TRUE,
89         TRUE,
90         FALSE,
91         FALSE,
92         FALSE,
93         FALSE,
94         FALSE,
95         FALSE,
96         FALSE,
97         FALSE,
98         FALSE,
99         FALSE,
100 };
101
102 /* True iff the rpc reply is an nfs status ONLY! */
103 static int nfsv2_repstat[NFS_NPROCS] = {
104         FALSE,
105         FALSE,
106         FALSE,
107         FALSE,
108         FALSE,
109         FALSE,
110         FALSE,
111         FALSE,
112         FALSE,
113         FALSE,
114         TRUE,
115         TRUE,
116         TRUE,
117         TRUE,
118         FALSE,
119         TRUE,
120         FALSE,
121         FALSE,
122 };
123
124 /*
125  * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
126  * within a (64, 2048) range. This is to prevent all mbuf clusters being
127  * tied up in the NFS dupreq cache for small values of nmbclusters.
128  */
129 static void
130 nfsrvcache_size_change(void)
131 {
132         desirednfsrvcache = nmbclusters / 2;
133         desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
134         desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
135 }
136
137 /*
138  * Initialize the server request cache list
139  */
140 void
141 nfsrv_initcache(void)
142 {
143         nfsrvcache_size_change();
144         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
145         TAILQ_INIT(&nfsrvlruhead);
146 }
147
148 /*
149  * Destroy the server request cache list
150  */
151 void
152 nfsrv_destroycache(void)
153 {
154         KKASSERT(TAILQ_EMPTY(&nfsrvlruhead));
155         hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
156 }
157
158 /*
159  * Look for the request in the cache
160  * If found then
161  *    return action and optionally reply
162  * else
163  *    insert it in the cache
164  *
165  * The rules are as follows:
166  * - if in progress, return DROP request
167  * - if completed within DELAY of the current time, return DROP it
168  * - if completed a longer time ago return REPLY if the reply was cached or
169  *   return DOIT
170  * Update/add new request at end of lru list
171  */
172 int
173 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
174                struct mbuf **repp)
175 {
176         struct nfsrvcache *rp;
177         struct mbuf *mb;
178         struct sockaddr_in *saddr;
179         caddr_t bpos;
180         int ret;
181
182         /*
183          * Don't cache recent requests for reliable transport protocols.
184          * (Maybe we should for the case of a reconnect, but..)
185          */
186         if (!nd->nd_nam2)
187                 return (RC_DOIT);
188
189         lwkt_gettoken(&srvcache_token);
190 loop:
191         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
192             rp = rp->rc_hash.le_next) {
193             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
194                 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
195                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
196                         if ((rp->rc_flag & RC_LOCKED) != 0) {
197                                 rp->rc_flag |= RC_WANTED;
198                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
199                                 goto loop;
200                         }
201                         rp->rc_flag |= RC_LOCKED;
202                         /* If not at end of LRU chain, move it there */
203                         if (TAILQ_NEXT(rp, rc_lru) != NULL) {
204                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
205                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
206                         }
207                         if (rp->rc_state == RC_UNUSED)
208                                 panic("nfsrv cache");
209                         if (rp->rc_state == RC_INPROG) {
210                                 nfsstats.srvcache_inproghits++;
211                                 ret = RC_DROPIT;
212                         } else if (rp->rc_flag & RC_REPSTATUS) {
213                                 nfsstats.srvcache_nonidemdonehits++;
214                                 nfs_rephead(0, nd, slp, rp->rc_status,
215                                             repp, &mb, &bpos);
216                                 ret = RC_REPLY;
217                         } else if (rp->rc_flag & RC_REPMBUF) {
218                                 nfsstats.srvcache_nonidemdonehits++;
219                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
220                                                 M_WAITOK);
221                                 ret = RC_REPLY;
222                         } else {
223                                 nfsstats.srvcache_idemdonehits++;
224                                 rp->rc_state = RC_INPROG;
225                                 ret = RC_DOIT;
226                         }
227                         rp->rc_flag &= ~RC_LOCKED;
228                         if (rp->rc_flag & RC_WANTED) {
229                                 rp->rc_flag &= ~RC_WANTED;
230                                 wakeup((caddr_t)rp);
231                         }
232                         lwkt_reltoken(&srvcache_token);
233                         return (ret);
234                 }
235         }
236
237         nfsstats.srvcache_misses++;
238         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
239         if (numnfsrvcache < desirednfsrvcache) {
240                 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
241                 numnfsrvcache++;
242                 rp->rc_flag = RC_LOCKED;
243         } else {
244                 rp = TAILQ_FIRST(&nfsrvlruhead);
245                 while ((rp->rc_flag & RC_LOCKED) != 0) {
246                         rp->rc_flag |= RC_WANTED;
247                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
248                         rp = TAILQ_FIRST(&nfsrvlruhead);
249                 }
250                 rp->rc_flag |= RC_LOCKED;
251                 LIST_REMOVE(rp, rc_hash);
252                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
253                 if (rp->rc_flag & RC_REPMBUF) {
254                         m_freem(rp->rc_reply);
255                         rp->rc_reply = NULL;
256                         rp->rc_flag &= ~RC_REPMBUF;
257                 }
258                 if (rp->rc_flag & RC_NAM) {
259                         kfree(rp->rc_nam, M_SONAME);
260                         rp->rc_nam = NULL;
261                         rp->rc_flag &= ~RC_NAM;
262                 }
263         }
264         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
265
266         rp->rc_state = RC_INPROG;
267         rp->rc_xid = nd->nd_retxid;
268         saddr = (struct sockaddr_in *)nd->nd_nam;
269         switch (saddr->sin_family) {
270         case AF_INET:
271                 rp->rc_flag |= RC_INETADDR;
272                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
273                 break;
274         default:
275                 rp->rc_flag |= RC_NAM;
276                 rp->rc_nam = dup_sockaddr(nd->nd_nam);
277                 break;
278         }
279         rp->rc_proc = nd->nd_procnum;
280         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
281         rp->rc_flag &= ~RC_LOCKED;
282         if (rp->rc_flag & RC_WANTED) {
283                 rp->rc_flag &= ~RC_WANTED;
284                 wakeup((caddr_t)rp);
285         }
286         lwkt_reltoken(&srvcache_token);
287
288         return (RC_DOIT);
289 }
290
291 /*
292  * Update a request cache entry after the rpc has been done
293  */
294 void
295 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
296 {
297         struct nfsrvcache *rp;
298
299         if (!nd->nd_nam2)
300                 return;
301
302         lwkt_gettoken(&srvcache_token);
303 loop:
304         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
305             rp = rp->rc_hash.le_next) {
306             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
307                 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
308                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
309                         if ((rp->rc_flag & RC_LOCKED) != 0) {
310                                 rp->rc_flag |= RC_WANTED;
311                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
312                                 goto loop;
313                         }
314                         rp->rc_flag |= RC_LOCKED;
315                         if (rp->rc_state == RC_DONE) {
316                                 /*
317                                  * This can occur if the cache is too small.
318                                  * Retransmits of the same request aren't 
319                                  * dropped so we may see the operation 
320                                  * complete more then once.
321                                  */
322                                 if (rp->rc_flag & RC_REPMBUF) {
323                                         m_freem(rp->rc_reply);
324                                         rp->rc_reply = NULL;
325                                         rp->rc_flag &= ~RC_REPMBUF;
326                                 }
327                         }
328                         rp->rc_state = RC_DONE;
329
330                         /*
331                          * If we have a valid reply update status and save
332                          * the reply for non-idempotent rpc's.
333                          */
334                         if (repvalid && nonidempotent[nd->nd_procnum]) {
335                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
336                                   nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
337                                         rp->rc_status = nd->nd_repstat;
338                                         rp->rc_flag |= RC_REPSTATUS;
339                                 } else {
340                                         if (rp->rc_flag & RC_REPMBUF) {
341                                                 m_freem(rp->rc_reply);
342                                                 rp->rc_reply = NULL;
343                                                 rp->rc_flag &= ~RC_REPMBUF;
344                                         }
345                                         rp->rc_reply = m_copym(repmbuf, 0,
346                                                         M_COPYALL, M_WAITOK);
347                                         rp->rc_flag |= RC_REPMBUF;
348                                 }
349                         }
350                         rp->rc_flag &= ~RC_LOCKED;
351                         if (rp->rc_flag & RC_WANTED) {
352                                 rp->rc_flag &= ~RC_WANTED;
353                                 wakeup((caddr_t)rp);
354                         }
355                         break;
356                 }
357         }
358         lwkt_reltoken(&srvcache_token);
359         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
360 }
361
362 /*
363  * Clean out the cache. Called when the last nfsd terminates.
364  */
365 void
366 nfsrv_cleancache(void)
367 {
368         struct nfsrvcache *rp;
369
370         lwkt_gettoken(&srvcache_token);
371         while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
372                 if (rp->rc_flag & RC_LOCKED) {
373                         rp->rc_flag |= RC_WANTED;
374                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
375                         continue;
376                 }
377                 LIST_REMOVE(rp, rc_hash);
378                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
379                 if (rp->rc_flag & RC_REPMBUF) {
380                         m_freem(rp->rc_reply);
381                         rp->rc_reply = NULL;
382                         rp->rc_flag &= ~RC_REPMBUF;
383                 }
384                 if (rp->rc_flag & RC_NAM) {
385                         kfree(rp->rc_nam, M_SONAME);
386                         rp->rc_nam = NULL;
387                         rp->rc_flag &= ~RC_NAM;
388                 }
389                 kfree(rp, M_NFSD);
390         }
391         numnfsrvcache = 0;
392         lwkt_reltoken(&srvcache_token);
393 }
394
395 #endif /* NFS_NOSERVER */