print_backtrace - Take parameter count
[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  * $DragonFly: src/sys/vfs/nfs/nfs_srvcache.c,v 1.13 2008/01/05 14:02:41 swildner Exp $
39  */
40
41 /*
42  * Reference: Chet Juszczak, "Improving the Performance and Correctness
43  *              of an NFS Server", in Proc. Winter 1989 USENIX Conference,
44  *              pages 53-63. San Diego, February 1989.
45  */
46 #include <sys/param.h>
47 #include <sys/malloc.h>
48 #include <sys/mount.h>
49 #include <sys/systm.h>
50 #include <sys/mbuf.h>
51 #include <sys/socket.h>
52 #include <sys/socketvar.h>      /* for dup_sockaddr */
53
54 #include <netinet/in.h>
55 #include "rpcv2.h"
56 #include "nfsproto.h"
57 #include "nfs.h"
58 #include "nfsrvcache.h"
59
60 #ifndef NFS_NOSERVER 
61 static long numnfsrvcache;
62 static long desirednfsrvcache = NFSRVCACHESIZ;
63
64 #define NFSRCHASH(xid) \
65         (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
66 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
67 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
68 static u_long nfsrvhash;
69
70 #define TRUE    1
71 #define FALSE   0
72
73 #define NETFAMILY(rp) \
74                 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
75
76 /*
77  * Static array that defines which nfs rpc's are nonidempotent
78  */
79 static int nonidempotent[NFS_NPROCS] = {
80         FALSE,
81         FALSE,
82         TRUE,
83         FALSE,
84         FALSE,
85         FALSE,
86         FALSE,
87         TRUE,
88         TRUE,
89         TRUE,
90         TRUE,
91         TRUE,
92         TRUE,
93         TRUE,
94         TRUE,
95         TRUE,
96         FALSE,
97         FALSE,
98         FALSE,
99         FALSE,
100         FALSE,
101         FALSE,
102         FALSE,
103         FALSE,
104         FALSE,
105         FALSE,
106 };
107
108 /* True iff the rpc reply is an nfs status ONLY! */
109 static int nfsv2_repstat[NFS_NPROCS] = {
110         FALSE,
111         FALSE,
112         FALSE,
113         FALSE,
114         FALSE,
115         FALSE,
116         FALSE,
117         FALSE,
118         FALSE,
119         FALSE,
120         TRUE,
121         TRUE,
122         TRUE,
123         TRUE,
124         FALSE,
125         TRUE,
126         FALSE,
127         FALSE,
128 };
129
130 /*
131  * Initialize the server request cache list
132  */
133 void
134 nfsrv_initcache(void)
135 {
136
137         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
138         TAILQ_INIT(&nfsrvlruhead);
139 }
140
141 /*
142  * Look for the request in the cache
143  * If found then
144  *    return action and optionally reply
145  * else
146  *    insert it in the cache
147  *
148  * The rules are as follows:
149  * - if in progress, return DROP request
150  * - if completed within DELAY of the current time, return DROP it
151  * - if completed a longer time ago return REPLY if the reply was cached or
152  *   return DOIT
153  * Update/add new request at end of lru list
154  */
155 int
156 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
157                struct mbuf **repp)
158 {
159         struct nfsrvcache *rp;
160         struct mbuf *mb;
161         struct sockaddr_in *saddr;
162         caddr_t bpos;
163         int ret;
164
165         /*
166          * Don't cache recent requests for reliable transport protocols.
167          * (Maybe we should for the case of a reconnect, but..)
168          */
169         if (!nd->nd_nam2)
170                 return (RC_DOIT);
171 loop:
172         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
173             rp = rp->rc_hash.le_next) {
174             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
175                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
176                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
177                         if ((rp->rc_flag & RC_LOCKED) != 0) {
178                                 rp->rc_flag |= RC_WANTED;
179                                 (void) tsleep((caddr_t)rp, 0, "nfsrc", 0);
180                                 goto loop;
181                         }
182                         rp->rc_flag |= RC_LOCKED;
183                         /* If not at end of LRU chain, move it there */
184                         if (TAILQ_NEXT(rp, rc_lru) != NULL) {
185                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
186                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
187                         }
188                         if (rp->rc_state == RC_UNUSED)
189                                 panic("nfsrv cache");
190                         if (rp->rc_state == RC_INPROG) {
191                                 nfsstats.srvcache_inproghits++;
192                                 ret = RC_DROPIT;
193                         } else if (rp->rc_flag & RC_REPSTATUS) {
194                                 nfsstats.srvcache_nonidemdonehits++;
195                                 nfs_rephead(0, nd, slp, rp->rc_status,
196                                             repp, &mb, &bpos);
197                                 ret = RC_REPLY;
198                         } else if (rp->rc_flag & RC_REPMBUF) {
199                                 nfsstats.srvcache_nonidemdonehits++;
200                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
201                                                 MB_WAIT);
202                                 ret = RC_REPLY;
203                         } else {
204                                 nfsstats.srvcache_idemdonehits++;
205                                 rp->rc_state = RC_INPROG;
206                                 ret = RC_DOIT;
207                         }
208                         rp->rc_flag &= ~RC_LOCKED;
209                         if (rp->rc_flag & RC_WANTED) {
210                                 rp->rc_flag &= ~RC_WANTED;
211                                 wakeup((caddr_t)rp);
212                         }
213                         return (ret);
214                 }
215         }
216         nfsstats.srvcache_misses++;
217         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
218         if (numnfsrvcache < desirednfsrvcache) {
219                 rp = (struct nfsrvcache *)kmalloc((u_long)sizeof *rp,
220                     M_NFSD, M_WAITOK | M_ZERO);
221                 numnfsrvcache++;
222                 rp->rc_flag = RC_LOCKED;
223         } else {
224                 rp = TAILQ_FIRST(&nfsrvlruhead);
225                 while ((rp->rc_flag & RC_LOCKED) != 0) {
226                         rp->rc_flag |= RC_WANTED;
227                         (void) tsleep((caddr_t)rp, 0, "nfsrc", 0);
228                         rp = TAILQ_FIRST(&nfsrvlruhead);
229                 }
230                 rp->rc_flag |= RC_LOCKED;
231                 LIST_REMOVE(rp, rc_hash);
232                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
233                 if (rp->rc_flag & RC_REPMBUF)
234                         m_freem(rp->rc_reply);
235                 if (rp->rc_flag & RC_NAM)
236                         FREE(rp->rc_nam, M_SONAME);
237                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
238         }
239         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
240         rp->rc_state = RC_INPROG;
241         rp->rc_xid = nd->nd_retxid;
242         saddr = (struct sockaddr_in *)nd->nd_nam;
243         switch (saddr->sin_family) {
244         case AF_INET:
245                 rp->rc_flag |= RC_INETADDR;
246                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
247                 break;
248         case AF_ISO:
249         default:
250                 rp->rc_flag |= RC_NAM;
251                 rp->rc_nam = dup_sockaddr(nd->nd_nam);
252                 break;
253         };
254         rp->rc_proc = nd->nd_procnum;
255         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
256         rp->rc_flag &= ~RC_LOCKED;
257         if (rp->rc_flag & RC_WANTED) {
258                 rp->rc_flag &= ~RC_WANTED;
259                 wakeup((caddr_t)rp);
260         }
261         return (RC_DOIT);
262 }
263
264 /*
265  * Update a request cache entry after the rpc has been done
266  */
267 void
268 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
269 {
270         struct nfsrvcache *rp;
271
272         if (!nd->nd_nam2)
273                 return;
274 loop:
275         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
276             rp = rp->rc_hash.le_next) {
277             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
278                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
279                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
280                         if ((rp->rc_flag & RC_LOCKED) != 0) {
281                                 rp->rc_flag |= RC_WANTED;
282                                 (void) tsleep((caddr_t)rp, 0, "nfsrc", 0);
283                                 goto loop;
284                         }
285                         rp->rc_flag |= RC_LOCKED;
286                         if (rp->rc_state == RC_DONE) {
287                                 /*
288                                  * This can occur if the cache is too small.
289                                  * Retransmits of the same request aren't 
290                                  * dropped so we may see the operation 
291                                  * complete more then once.
292                                  */
293                                 if (rp->rc_flag & RC_REPMBUF) {
294                                         m_freem(rp->rc_reply);
295                                         rp->rc_flag &= ~RC_REPMBUF;
296                                 }
297                         }
298                         rp->rc_state = RC_DONE;
299                         /*
300                          * If we have a valid reply update status and save
301                          * the reply for non-idempotent rpc's.
302                          */
303                         if (repvalid && nonidempotent[nd->nd_procnum]) {
304                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
305                                   nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
306                                         rp->rc_status = nd->nd_repstat;
307                                         rp->rc_flag |= RC_REPSTATUS;
308                                 } else {
309                                         rp->rc_reply = m_copym(repmbuf,
310                                                 0, M_COPYALL, MB_WAIT);
311                                         rp->rc_flag |= RC_REPMBUF;
312                                 }
313                         }
314                         rp->rc_flag &= ~RC_LOCKED;
315                         if (rp->rc_flag & RC_WANTED) {
316                                 rp->rc_flag &= ~RC_WANTED;
317                                 wakeup((caddr_t)rp);
318                         }
319                         return;
320                 }
321         }
322         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
323 }
324
325 /*
326  * Clean out the cache. Called when the last nfsd terminates.
327  */
328 void
329 nfsrv_cleancache(void)
330 {
331         struct nfsrvcache *rp, *nextrp;
332
333         TAILQ_FOREACH_MUTABLE(rp, &nfsrvlruhead, rc_lru, nextrp) {
334                 LIST_REMOVE(rp, rc_hash);
335                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
336                 if (rp->rc_flag & RC_REPMBUF)
337                         m_freem(rp->rc_reply);
338                 if (rp->rc_flag & RC_NAM)
339                         kfree(rp->rc_nam, M_SONAME);
340                 kfree(rp, M_NFSD);
341         }
342         numnfsrvcache = 0;
343 }
344
345 #endif /* NFS_NOSERVER */