]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/nfsserver/nfs_srvcache.c
This commit was generated by cvs2svn to compensate for changes in r163299,
[FreeBSD/FreeBSD.git] / sys / nfsserver / 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  * 4. 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  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 /*
39  * Reference: Chet Juszczak, "Improving the Performance and Correctness
40  *              of an NFS Server", in Proc. Winter 1989 USENIX Conference,
41  *              pages 53-63. San Diego, February 1989.
42  */
43 #include <sys/param.h>
44 #include <sys/malloc.h>
45 #include <sys/mount.h>
46 #include <sys/systm.h>
47 #include <sys/lock.h>
48 #include <sys/mbuf.h>
49 #include <sys/mutex.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>      /* for sodupsockaddr */
52 #include <sys/eventhandler.h>
53
54 #include <netinet/in.h>
55 #include <nfs/rpcv2.h>
56 #include <nfs/nfsproto.h>
57 #include <nfsserver/nfs.h>
58 #include <nfsserver/nfsrvcache.h>
59
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 static eventhandler_tag nfsrv_nmbclusters_tag;
69
70 #define TRUE    1
71 #define FALSE   0
72
73 #define NETFAMILY(rp) \
74                 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
75
76 /*
77  * Static array that defines which nfs rpc's are nonidempotent
78  */
79 static const 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 };
104
105 /* True iff the rpc reply is an nfs status ONLY! */
106 static const 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 nmbclsters, floating 
129  * within a (64, 2048) range. This is to prevent all mbuf clusters being tied up 
130  * in the NFS dupreq cache for small values of nmbclusters. 
131  */
132 static void
133 nfsrvcache_size_change(void *tag)
134 {
135         desirednfsrvcache = nmbclusters /2;
136         if (desirednfsrvcache > NFSRVCACHE_MAX_SIZE)
137                 desirednfsrvcache = NFSRVCACHE_MAX_SIZE;
138         if (desirednfsrvcache < NFSRVCACHE_MIN_SIZE)
139                 desirednfsrvcache = NFSRVCACHE_MIN_SIZE;        
140 }
141
142 /*
143  * Initialize the server request cache list
144  */
145 void
146 nfsrv_initcache(void)
147 {
148         nfsrvcache_size_change(NULL);
149         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
150         TAILQ_INIT(&nfsrvlruhead);
151         nfsrv_nmbclusters_tag = EVENTHANDLER_REGISTER(nmbclusters_change,
152             nfsrvcache_size_change, NULL, EVENTHANDLER_PRI_FIRST);
153 }
154
155 /*
156  * Teardown the server request cache list
157  */
158 void
159 nfsrv_destroycache(void)
160 {
161         KASSERT(TAILQ_EMPTY(&nfsrvlruhead), ("%s: pending requests", __func__));
162         EVENTHANDLER_DEREGISTER(nmbclusters_change, nfsrv_nmbclusters_tag);
163         hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
164 }
165
166 /*
167  * Look for the request in the cache
168  * If found then
169  *    return action and optionally reply
170  * else
171  *    insert it in the cache
172  *
173  * The rules are as follows:
174  * - if in progress, return DROP request
175  * - if completed within DELAY of the current time, return DROP it
176  * - if completed a longer time ago return REPLY if the reply was cached or
177  *   return DOIT
178  * Update/add new request at end of lru list
179  */
180 int
181 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
182 {
183         struct nfsrvcache *rp;
184         struct mbuf *mb;
185         struct sockaddr_in *saddr;
186         caddr_t bpos;
187         int ret;
188
189         NFSD_LOCK_ASSERT();
190
191         /*
192          * Don't cache recent requests for reliable transport protocols.
193          * (Maybe we should for the case of a reconnect, but..)
194          */
195         if (!nd->nd_nam2)
196                 return (RC_DOIT);
197 loop:
198         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
199             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
200                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
201                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
202                         if ((rp->rc_flag & RC_LOCKED) != 0) {
203                                 rp->rc_flag |= RC_WANTED;
204                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
205                                     "nfsrc", 0);
206                                 goto loop;
207                         }
208                         rp->rc_flag |= RC_LOCKED;
209                         /* If not at end of LRU chain, move it there */
210                         if (TAILQ_NEXT(rp, rc_lru)) {
211                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
212                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
213                         }
214                         if (rp->rc_state == RC_UNUSED)
215                                 panic("nfsrv cache");
216                         if (rp->rc_state == RC_INPROG) {
217                                 nfsrvstats.srvcache_inproghits++;
218                                 ret = RC_DROPIT;
219                         } else if (rp->rc_flag & RC_REPSTATUS) {
220                                 nfsrvstats.srvcache_nonidemdonehits++;
221                                 *repp = nfs_rephead(0, nd, rp->rc_status,
222                                     &mb, &bpos);
223                                 ret = RC_REPLY;
224                         } else if (rp->rc_flag & RC_REPMBUF) {
225                                 nfsrvstats.srvcache_nonidemdonehits++;
226                                 NFSD_UNLOCK();
227                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
228                                                 M_TRYWAIT);
229                                 NFSD_LOCK();
230                                 ret = RC_REPLY;
231                         } else {
232                                 nfsrvstats.srvcache_idemdonehits++;
233                                 rp->rc_state = RC_INPROG;
234                                 ret = RC_DOIT;
235                         }
236                         rp->rc_flag &= ~RC_LOCKED;
237                         if (rp->rc_flag & RC_WANTED) {
238                                 rp->rc_flag &= ~RC_WANTED;
239                                 wakeup(rp);
240                         }
241                         return (ret);
242                 }
243         }
244         nfsrvstats.srvcache_misses++;
245         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
246         if (numnfsrvcache < desirednfsrvcache) {
247                 NFSD_UNLOCK();
248                 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
249                     M_NFSD, M_WAITOK | M_ZERO);
250                 NFSD_LOCK();
251                 numnfsrvcache++;
252                 rp->rc_flag = RC_LOCKED;
253         } else {
254                 rp = TAILQ_FIRST(&nfsrvlruhead);
255                 while ((rp->rc_flag & RC_LOCKED) != 0) {
256                         rp->rc_flag |= RC_WANTED;
257                         (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
258                         rp = TAILQ_FIRST(&nfsrvlruhead);
259                 }
260                 rp->rc_flag |= RC_LOCKED;
261                 LIST_REMOVE(rp, rc_hash);
262                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
263                 if (rp->rc_flag & RC_REPMBUF)
264                         m_freem(rp->rc_reply);
265                 if (rp->rc_flag & RC_NAM)
266                         FREE(rp->rc_nam, M_SONAME);
267                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
268         }
269         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
270         rp->rc_state = RC_INPROG;
271         rp->rc_xid = nd->nd_retxid;
272         saddr = (struct sockaddr_in *)nd->nd_nam;
273         switch (saddr->sin_family) {
274         case AF_INET:
275                 rp->rc_flag |= RC_INETADDR;
276                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
277                 break;
278 /*      case AF_INET6:  */
279 /*      case AF_ISO:    */
280         default:
281                 /*
282                  * XXXRW: Seems like we should only set RC_NAM if we
283                  * actually manage to set rc_nam to something non-NULL.
284                  */
285                 rp->rc_flag |= RC_NAM;
286                 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
287                 break;
288         };
289         rp->rc_proc = nd->nd_procnum;
290         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
291         rp->rc_flag &= ~RC_LOCKED;
292         if (rp->rc_flag & RC_WANTED) {
293                 rp->rc_flag &= ~RC_WANTED;
294                 wakeup(rp);
295         }
296         return (RC_DOIT);
297 }
298
299 /*
300  * Update a request cache entry after the rpc has been done
301  */
302 void
303 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
304 {
305         struct nfsrvcache *rp;
306
307         NFSD_LOCK_ASSERT();
308
309         if (!nd->nd_nam2)
310                 return;
311 loop:
312         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
313             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
314                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
315                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
316                         if ((rp->rc_flag & RC_LOCKED) != 0) {
317                                 rp->rc_flag |= RC_WANTED;
318                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
319                                     "nfsrc", 0);
320                                 goto loop;
321                         }
322                         rp->rc_flag |= RC_LOCKED;
323                         if (rp->rc_state == RC_DONE) {
324                                 /*
325                                  * This can occur if the cache is too small.
326                                  * Retransmits of the same request aren't
327                                  * dropped so we may see the operation
328                                  * complete more then once.
329                                  */
330                                 if (rp->rc_flag & RC_REPMBUF) {
331                                         m_freem(rp->rc_reply);
332                                         rp->rc_flag &= ~RC_REPMBUF;
333                                 }
334                         }
335                         rp->rc_state = RC_DONE;
336                         /*
337                          * If we have a valid reply update status and save
338                          * the reply for non-idempotent rpc's.
339                          */
340                         if (repvalid && nonidempotent[nd->nd_procnum]) {
341                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
342                                     nfsv2_repstat[
343                                     nfsrvv2_procid[nd->nd_procnum]]) {
344                                         rp->rc_status = nd->nd_repstat;
345                                         rp->rc_flag |= RC_REPSTATUS;
346                                 } else {
347                                         NFSD_UNLOCK();
348                                         rp->rc_reply = m_copym(repmbuf,
349                                                 0, M_COPYALL, M_TRYWAIT);
350                                         NFSD_LOCK();
351                                         rp->rc_flag |= RC_REPMBUF;
352                                 }
353                         }
354                         rp->rc_flag &= ~RC_LOCKED;
355                         if (rp->rc_flag & RC_WANTED) {
356                                 rp->rc_flag &= ~RC_WANTED;
357                                 wakeup(rp);
358                         }
359                         return;
360                 }
361         }
362         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
363 }
364
365 /*
366  * Clean out the cache. Called when the last nfsd terminates.
367  */
368 void
369 nfsrv_cleancache(void)
370 {
371         struct nfsrvcache *rp, *nextrp;
372
373         NFSD_LOCK_ASSERT();
374
375         TAILQ_FOREACH_SAFE(rp, &nfsrvlruhead, rc_lru, nextrp) {
376                 LIST_REMOVE(rp, rc_hash);
377                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
378                 if (rp->rc_flag & RC_REPMBUF)
379                         m_freem(rp->rc_reply);
380                 if (rp->rc_flag & RC_NAM)
381                         free(rp->rc_nam, M_SONAME);
382                 free(rp, M_NFSD);
383         }
384         numnfsrvcache = 0;
385 }