Merge branch 'vendor/NVI2'
[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  * Look for the request in the cache
150  * If found then
151  *    return action and optionally reply
152  * else
153  *    insert it in the cache
154  *
155  * The rules are as follows:
156  * - if in progress, return DROP request
157  * - if completed within DELAY of the current time, return DROP it
158  * - if completed a longer time ago return REPLY if the reply was cached or
159  *   return DOIT
160  * Update/add new request at end of lru list
161  */
162 int
163 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp,
164                struct mbuf **repp)
165 {
166         struct nfsrvcache *rp;
167         struct mbuf *mb;
168         struct sockaddr_in *saddr;
169         caddr_t bpos;
170         int ret;
171
172         /*
173          * Don't cache recent requests for reliable transport protocols.
174          * (Maybe we should for the case of a reconnect, but..)
175          */
176         if (!nd->nd_nam2)
177                 return (RC_DOIT);
178
179         lwkt_gettoken(&srvcache_token);
180 loop:
181         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
182             rp = rp->rc_hash.le_next) {
183             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
184                 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
185                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
186                         if ((rp->rc_flag & RC_LOCKED) != 0) {
187                                 rp->rc_flag |= RC_WANTED;
188                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
189                                 goto loop;
190                         }
191                         rp->rc_flag |= RC_LOCKED;
192                         /* If not at end of LRU chain, move it there */
193                         if (TAILQ_NEXT(rp, rc_lru) != NULL) {
194                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
195                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
196                         }
197                         if (rp->rc_state == RC_UNUSED)
198                                 panic("nfsrv cache");
199                         if (rp->rc_state == RC_INPROG) {
200                                 nfsstats.srvcache_inproghits++;
201                                 ret = RC_DROPIT;
202                         } else if (rp->rc_flag & RC_REPSTATUS) {
203                                 nfsstats.srvcache_nonidemdonehits++;
204                                 nfs_rephead(0, nd, slp, rp->rc_status,
205                                             repp, &mb, &bpos);
206                                 ret = RC_REPLY;
207                         } else if (rp->rc_flag & RC_REPMBUF) {
208                                 nfsstats.srvcache_nonidemdonehits++;
209                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
210                                                 M_WAITOK);
211                                 ret = RC_REPLY;
212                         } else {
213                                 nfsstats.srvcache_idemdonehits++;
214                                 rp->rc_state = RC_INPROG;
215                                 ret = RC_DOIT;
216                         }
217                         rp->rc_flag &= ~RC_LOCKED;
218                         if (rp->rc_flag & RC_WANTED) {
219                                 rp->rc_flag &= ~RC_WANTED;
220                                 wakeup((caddr_t)rp);
221                         }
222                         lwkt_reltoken(&srvcache_token);
223                         return (ret);
224                 }
225         }
226
227         nfsstats.srvcache_misses++;
228         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
229         if (numnfsrvcache < desirednfsrvcache) {
230                 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO);
231                 numnfsrvcache++;
232                 rp->rc_flag = RC_LOCKED;
233         } else {
234                 rp = TAILQ_FIRST(&nfsrvlruhead);
235                 while ((rp->rc_flag & RC_LOCKED) != 0) {
236                         rp->rc_flag |= RC_WANTED;
237                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
238                         rp = TAILQ_FIRST(&nfsrvlruhead);
239                 }
240                 rp->rc_flag |= RC_LOCKED;
241                 LIST_REMOVE(rp, rc_hash);
242                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
243                 if (rp->rc_flag & RC_REPMBUF) {
244                         m_freem(rp->rc_reply);
245                         rp->rc_reply = NULL;
246                         rp->rc_flag &= ~RC_REPMBUF;
247                 }
248                 if (rp->rc_flag & RC_NAM) {
249                         kfree(rp->rc_nam, M_SONAME);
250                         rp->rc_nam = NULL;
251                         rp->rc_flag &= ~RC_NAM;
252                 }
253         }
254         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
255
256         rp->rc_state = RC_INPROG;
257         rp->rc_xid = nd->nd_retxid;
258         saddr = (struct sockaddr_in *)nd->nd_nam;
259         switch (saddr->sin_family) {
260         case AF_INET:
261                 rp->rc_flag |= RC_INETADDR;
262                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
263                 break;
264         default:
265                 rp->rc_flag |= RC_NAM;
266                 rp->rc_nam = dup_sockaddr(nd->nd_nam);
267                 break;
268         }
269         rp->rc_proc = nd->nd_procnum;
270         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
271         rp->rc_flag &= ~RC_LOCKED;
272         if (rp->rc_flag & RC_WANTED) {
273                 rp->rc_flag &= ~RC_WANTED;
274                 wakeup((caddr_t)rp);
275         }
276         lwkt_reltoken(&srvcache_token);
277
278         return (RC_DOIT);
279 }
280
281 /*
282  * Update a request cache entry after the rpc has been done
283  */
284 void
285 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
286 {
287         struct nfsrvcache *rp;
288
289         if (!nd->nd_nam2)
290                 return;
291
292         lwkt_gettoken(&srvcache_token);
293 loop:
294         for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL;
295             rp = rp->rc_hash.le_next) {
296             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
297                 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
298                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
299                         if ((rp->rc_flag & RC_LOCKED) != 0) {
300                                 rp->rc_flag |= RC_WANTED;
301                                 tsleep((caddr_t)rp, 0, "nfsrc", 0);
302                                 goto loop;
303                         }
304                         rp->rc_flag |= RC_LOCKED;
305                         if (rp->rc_state == RC_DONE) {
306                                 /*
307                                  * This can occur if the cache is too small.
308                                  * Retransmits of the same request aren't 
309                                  * dropped so we may see the operation 
310                                  * complete more then once.
311                                  */
312                                 if (rp->rc_flag & RC_REPMBUF) {
313                                         m_freem(rp->rc_reply);
314                                         rp->rc_reply = NULL;
315                                         rp->rc_flag &= ~RC_REPMBUF;
316                                 }
317                         }
318                         rp->rc_state = RC_DONE;
319
320                         /*
321                          * If we have a valid reply update status and save
322                          * the reply for non-idempotent rpc's.
323                          */
324                         if (repvalid && nonidempotent[nd->nd_procnum]) {
325                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
326                                   nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
327                                         rp->rc_status = nd->nd_repstat;
328                                         rp->rc_flag |= RC_REPSTATUS;
329                                 } else {
330                                         if (rp->rc_flag & RC_REPMBUF) {
331                                                 m_freem(rp->rc_reply);
332                                                 rp->rc_reply = NULL;
333                                                 rp->rc_flag &= ~RC_REPMBUF;
334                                         }
335                                         rp->rc_reply = m_copym(repmbuf, 0,
336                                                         M_COPYALL, M_WAITOK);
337                                         rp->rc_flag |= RC_REPMBUF;
338                                 }
339                         }
340                         rp->rc_flag &= ~RC_LOCKED;
341                         if (rp->rc_flag & RC_WANTED) {
342                                 rp->rc_flag &= ~RC_WANTED;
343                                 wakeup((caddr_t)rp);
344                         }
345                         break;
346                 }
347         }
348         lwkt_reltoken(&srvcache_token);
349         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
350 }
351
352 /*
353  * Clean out the cache. Called when the last nfsd terminates.
354  */
355 void
356 nfsrv_cleancache(void)
357 {
358         struct nfsrvcache *rp;
359
360         lwkt_gettoken(&srvcache_token);
361         while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
362                 if (rp->rc_flag & RC_LOCKED) {
363                         rp->rc_flag |= RC_WANTED;
364                         tsleep((caddr_t)rp, 0, "nfsrc", 0);
365                         continue;
366                 }
367                 LIST_REMOVE(rp, rc_hash);
368                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
369                 if (rp->rc_flag & RC_REPMBUF) {
370                         m_freem(rp->rc_reply);
371                         rp->rc_reply = NULL;
372                         rp->rc_flag &= ~RC_REPMBUF;
373                 }
374                 if (rp->rc_flag & RC_NAM) {
375                         kfree(rp->rc_nam, M_SONAME);
376                         rp->rc_nam = NULL;
377                         rp->rc_flag &= ~RC_NAM;
378                 }
379                 kfree(rp, M_NFSD);
380         }
381         numnfsrvcache = 0;
382         lwkt_reltoken(&srvcache_token);
383 }
384
385 #endif /* NFS_NOSERVER */