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