xref: /dragonfly/sys/vfs/nfs/nfs_srvcache.c (revision f4aeef67f55881dd79267697a869634dcfbf3590)
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
nfsrvcache_size_change(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
nfsrv_initcache(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
nfsrv_destroycache(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
nfsrv_getcache(struct nfsrv_descript * nd,struct nfssvc_sock * slp,struct mbuf ** repp)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
nfsrv_updatecache(struct nfsrv_descript * nd,int repvalid,struct mbuf * repmbuf)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
nfsrv_cleancache(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 */
396