Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[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 #define NETFAMILY(rp) \
69                 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
70
71 struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token);
72
73 /*
74  * Static array that defines which nfs rpc's are nonidempotent
75  */
76 static int nonidempotent[NFS_NPROCS] = {
77         FALSE,
78         FALSE,
79         TRUE,
80         FALSE,
81         FALSE,
82         FALSE,
83         FALSE,
84         TRUE,
85         TRUE,
86         TRUE,
87         TRUE,
88         TRUE,
89         TRUE,
90         TRUE,
91         TRUE,
92         TRUE,
93         FALSE,
94         FALSE,
95         FALSE,
96         FALSE,
97         FALSE,
98         FALSE,
99         FALSE,
100         FALSE,
101         FALSE,
102         FALSE,
103 };
104
105 /* True iff the rpc reply is an nfs status ONLY! */
106 static int nfsv2_repstat[NFS_NPROCS] = {
107         FALSE,
108         FALSE,
109         FALSE,
110         FALSE,
111         FALSE,
112         FALSE,
113         FALSE,
114         FALSE,
115         FALSE,
116         FALSE,
117         TRUE,
118         TRUE,
119         TRUE,
120         TRUE,
121         FALSE,
122         TRUE,
123         FALSE,
124         FALSE,
125 };
126
127 /*
128  * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
129  * within a (64, 2048) range. This is to prevent all mbuf clusters being
130  * tied up in the NFS dupreq cache for small values of nmbclusters.
131  */
132 static void
133 nfsrvcache_size_change(void)
134 {
135         desirednfsrvcache = nmbclusters / 2;
136         desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
137         desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
138 }
139
140 /*
141  * Initialize the server request cache list
142  */
143 void
144 nfsrv_initcache(void)
145 {
146         nfsrvcache_size_change();
147         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
148         TAILQ_INIT(&nfsrvlruhead);
149 }
150
151 /*
152  * Look for the request in the cache
153  * If found then
154  *    return action and optionally reply
155  * else
156  *    insert it in the cache
157  *
158  * The rules are as follows:
159  * - if in progress, return DROP request
160  * - if completed within DELAY of the current time, return DROP it
161  * - if completed a longer time ago return REPLY if the reply was cached or
162  *   return DOIT
163  * Update/add new request at end of lru list
164  */
165 int
166 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
167                struct mbuf **repp)
168 {
169         struct nfsrvcache *rp;
170         struct mbuf *mb;
171         struct sockaddr_in *saddr;
172         caddr_t bpos;
173         int ret;
174
175         /*
176          * Don't cache recent requests for reliable transport protocols.
177          * (Maybe we should for the case of a reconnect, but..)
178          */
179         if (!nd->nd_nam2)
180                 return (RC_DOIT);
181
182         lwkt_gettoken(&srvcache_token);
183 loop:
184         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
185             rp = rp->rc_hash.le_next) {
186             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
187                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
188                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
189                         if ((rp->rc_flag & RC_LOCKED) != 0) {
190                                 rp->rc_flag |= RC_WANTED;
191                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
192                                 goto loop;
193                         }
194                         rp->rc_flag |= RC_LOCKED;
195                         /* If not at end of LRU chain, move it there */
196                         if (TAILQ_NEXT(rp, rc_lru) != NULL) {
197                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
198                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
199                         }
200                         if (rp->rc_state == RC_UNUSED)
201                                 panic("nfsrv cache");
202                         if (rp->rc_state == RC_INPROG) {
203                                 nfsstats.srvcache_inproghits++;
204                                 ret = RC_DROPIT;
205                         } else if (rp->rc_flag & RC_REPSTATUS) {
206                                 nfsstats.srvcache_nonidemdonehits++;
207                                 nfs_rephead(0, nd, slp, rp->rc_status,
208                                             repp, &mb, &bpos);
209                                 ret = RC_REPLY;
210                         } else if (rp->rc_flag & RC_REPMBUF) {
211                                 nfsstats.srvcache_nonidemdonehits++;
212                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
213                                                 MB_WAIT);
214                                 ret = RC_REPLY;
215                         } else {
216                                 nfsstats.srvcache_idemdonehits++;
217                                 rp->rc_state = RC_INPROG;
218                                 ret = RC_DOIT;
219                         }
220                         rp->rc_flag &= ~RC_LOCKED;
221                         if (rp->rc_flag & RC_WANTED) {
222                                 rp->rc_flag &= ~RC_WANTED;
223                                 wakeup((caddr_t)rp);
224                         }
225                         lwkt_reltoken(&srvcache_token);
226                         return (ret);
227                 }
228         }
229
230         nfsstats.srvcache_misses++;
231         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
232         if (numnfsrvcache < desirednfsrvcache) {
233                 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
234                 numnfsrvcache++;
235                 rp->rc_flag = RC_LOCKED;
236         } else {
237                 rp = TAILQ_FIRST(&nfsrvlruhead);
238                 while ((rp->rc_flag & RC_LOCKED) != 0) {
239                         rp->rc_flag |= RC_WANTED;
240                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
241                         rp = TAILQ_FIRST(&nfsrvlruhead);
242                 }
243                 rp->rc_flag |= RC_LOCKED;
244                 LIST_REMOVE(rp, rc_hash);
245                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
246                 if (rp->rc_flag & RC_REPMBUF) {
247                         m_freem(rp->rc_reply);
248                         rp->rc_reply = NULL;
249                         rp->rc_flag &= ~RC_REPMBUF;
250                 }
251                 if (rp->rc_flag & RC_NAM) {
252                         kfree(rp->rc_nam, M_SONAME);
253                         rp->rc_nam = NULL;
254                         rp->rc_flag &= ~RC_NAM;
255                 }
256         }
257         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
258
259         rp->rc_state = RC_INPROG;
260         rp->rc_xid = nd->nd_retxid;
261         saddr = (struct sockaddr_in *)nd->nd_nam;
262         switch (saddr->sin_family) {
263         case AF_INET:
264                 rp->rc_flag |= RC_INETADDR;
265                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
266                 break;
267         case AF_ISO:
268         default:
269                 rp->rc_flag |= RC_NAM;
270                 rp->rc_nam = dup_sockaddr(nd->nd_nam);
271                 break;
272         };
273         rp->rc_proc = nd->nd_procnum;
274         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
275         rp->rc_flag &= ~RC_LOCKED;
276         if (rp->rc_flag & RC_WANTED) {
277                 rp->rc_flag &= ~RC_WANTED;
278                 wakeup((caddr_t)rp);
279         }
280         lwkt_reltoken(&srvcache_token);
281
282         return (RC_DOIT);
283 }
284
285 /*
286  * Update a request cache entry after the rpc has been done
287  */
288 void
289 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
290 {
291         struct nfsrvcache *rp;
292
293         if (!nd->nd_nam2)
294                 return;
295
296         lwkt_gettoken(&srvcache_token);
297 loop:
298         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
299             rp = rp->rc_hash.le_next) {
300             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
301                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
302                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
303                         if ((rp->rc_flag & RC_LOCKED) != 0) {
304                                 rp->rc_flag |= RC_WANTED;
305                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
306                                 goto loop;
307                         }
308                         rp->rc_flag |= RC_LOCKED;
309                         if (rp->rc_state == RC_DONE) {
310                                 /*
311                                  * This can occur if the cache is too small.
312                                  * Retransmits of the same request aren't 
313                                  * dropped so we may see the operation 
314                                  * complete more then once.
315                                  */
316                                 if (rp->rc_flag & RC_REPMBUF) {
317                                         m_freem(rp->rc_reply);
318                                         rp->rc_reply = NULL;
319                                         rp->rc_flag &= ~RC_REPMBUF;
320                                 }
321                         }
322                         rp->rc_state = RC_DONE;
323
324                         /*
325                          * If we have a valid reply update status and save
326                          * the reply for non-idempotent rpc's.
327                          */
328                         if (repvalid && nonidempotent[nd->nd_procnum]) {
329                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
330                                   nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
331                                         rp->rc_status = nd->nd_repstat;
332                                         rp->rc_flag |= RC_REPSTATUS;
333                                 } else {
334                                         if (rp->rc_flag & RC_REPMBUF) {
335                                                 m_freem(rp->rc_reply);
336                                                 rp->rc_reply = NULL;
337                                                 rp->rc_flag &= ~RC_REPMBUF;
338                                         }
339                                         rp->rc_reply = m_copym(repmbuf, 0,
340                                                         M_COPYALL, MB_WAIT);
341                                         rp->rc_flag |= RC_REPMBUF;
342                                 }
343                         }
344                         rp->rc_flag &= ~RC_LOCKED;
345                         if (rp->rc_flag & RC_WANTED) {
346                                 rp->rc_flag &= ~RC_WANTED;
347                                 wakeup((caddr_t)rp);
348                         }
349                         break;
350                 }
351         }
352         lwkt_reltoken(&srvcache_token);
353         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
354 }
355
356 /*
357  * Clean out the cache. Called when the last nfsd terminates.
358  */
359 void
360 nfsrv_cleancache(void)
361 {
362         struct nfsrvcache *rp;
363
364         lwkt_gettoken(&srvcache_token);
365         while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
366                 if (rp->rc_flag & RC_LOCKED) {
367                         rp->rc_flag |= RC_WANTED;
368                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
369                         continue;
370                 }
371                 LIST_REMOVE(rp, rc_hash);
372                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
373                 if (rp->rc_flag & RC_REPMBUF) {
374                         m_freem(rp->rc_reply);
375                         rp->rc_reply = NULL;
376                         rp->rc_flag &= ~RC_REPMBUF;
377                 }
378                 if (rp->rc_flag & RC_NAM) {
379                         kfree(rp->rc_nam, M_SONAME);
380                         rp->rc_nam = NULL;
381                         rp->rc_flag &= ~RC_NAM;
382                 }
383                 kfree(rp, M_NFSD);
384         }
385         numnfsrvcache = 0;
386         lwkt_reltoken(&srvcache_token);
387 }
388
389 #endif /* NFS_NOSERVER */