]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/nfsserver/nfs_srvcache.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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                                 NFSD_UNLOCK();
222                                 *repp = nfs_rephead(0, nd, rp->rc_status,
223                                     &mb, &bpos);
224                                 ret = RC_REPLY;
225                                 NFSD_LOCK();
226                         } else if (rp->rc_flag & RC_REPMBUF) {
227                                 nfsrvstats.srvcache_nonidemdonehits++;
228                                 NFSD_UNLOCK();
229                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
230                                                 M_TRYWAIT);
231                                 NFSD_LOCK();
232                                 ret = RC_REPLY;
233                         } else {
234                                 nfsrvstats.srvcache_idemdonehits++;
235                                 rp->rc_state = RC_INPROG;
236                                 ret = RC_DOIT;
237                         }
238                         rp->rc_flag &= ~RC_LOCKED;
239                         if (rp->rc_flag & RC_WANTED) {
240                                 rp->rc_flag &= ~RC_WANTED;
241                                 wakeup(rp);
242                         }
243                         return (ret);
244                 }
245         }
246         nfsrvstats.srvcache_misses++;
247         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
248         if (numnfsrvcache < desirednfsrvcache) {
249                 NFSD_UNLOCK();
250                 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
251                     M_NFSD, M_WAITOK | M_ZERO);
252                 NFSD_LOCK();
253                 numnfsrvcache++;
254                 rp->rc_flag = RC_LOCKED;
255         } else {
256                 rp = TAILQ_FIRST(&nfsrvlruhead);
257                 while ((rp->rc_flag & RC_LOCKED) != 0) {
258                         rp->rc_flag |= RC_WANTED;
259                         (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
260                         rp = TAILQ_FIRST(&nfsrvlruhead);
261                 }
262                 rp->rc_flag |= RC_LOCKED;
263                 LIST_REMOVE(rp, rc_hash);
264                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
265                 if (rp->rc_flag & RC_REPMBUF)
266                         m_freem(rp->rc_reply);
267                 if (rp->rc_flag & RC_NAM)
268                         FREE(rp->rc_nam, M_SONAME);
269                 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
270         }
271         TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
272         rp->rc_state = RC_INPROG;
273         rp->rc_xid = nd->nd_retxid;
274         saddr = (struct sockaddr_in *)nd->nd_nam;
275         switch (saddr->sin_family) {
276         case AF_INET:
277                 rp->rc_flag |= RC_INETADDR;
278                 rp->rc_inetaddr = saddr->sin_addr.s_addr;
279                 break;
280 /*      case AF_INET6:  */
281 /*      case AF_ISO:    */
282         default:
283                 /*
284                  * XXXRW: Seems like we should only set RC_NAM if we
285                  * actually manage to set rc_nam to something non-NULL.
286                  */
287                 rp->rc_flag |= RC_NAM;
288                 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
289                 break;
290         };
291         rp->rc_proc = nd->nd_procnum;
292         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
293         rp->rc_flag &= ~RC_LOCKED;
294         if (rp->rc_flag & RC_WANTED) {
295                 rp->rc_flag &= ~RC_WANTED;
296                 wakeup(rp);
297         }
298         return (RC_DOIT);
299 }
300
301 /*
302  * Update a request cache entry after the rpc has been done
303  */
304 void
305 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
306 {
307         struct nfsrvcache *rp;
308
309         NFSD_LOCK_ASSERT();
310
311         if (!nd->nd_nam2)
312                 return;
313 loop:
314         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
315             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
316                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
317                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
318                         if ((rp->rc_flag & RC_LOCKED) != 0) {
319                                 rp->rc_flag |= RC_WANTED;
320                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
321                                     "nfsrc", 0);
322                                 goto loop;
323                         }
324                         rp->rc_flag |= RC_LOCKED;
325                         if (rp->rc_state == RC_DONE) {
326                                 /*
327                                  * This can occur if the cache is too small.
328                                  * Retransmits of the same request aren't
329                                  * dropped so we may see the operation
330                                  * complete more then once.
331                                  */
332                                 if (rp->rc_flag & RC_REPMBUF) {
333                                         m_freem(rp->rc_reply);
334                                         rp->rc_flag &= ~RC_REPMBUF;
335                                 }
336                         }
337                         rp->rc_state = RC_DONE;
338                         /*
339                          * If we have a valid reply update status and save
340                          * the reply for non-idempotent rpc's.
341                          */
342                         if (repvalid && nonidempotent[nd->nd_procnum]) {
343                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
344                                     nfsv2_repstat[
345                                     nfsrvv2_procid[nd->nd_procnum]]) {
346                                         rp->rc_status = nd->nd_repstat;
347                                         rp->rc_flag |= RC_REPSTATUS;
348                                 } else {
349                                         NFSD_UNLOCK();
350                                         rp->rc_reply = m_copym(repmbuf,
351                                                 0, M_COPYALL, M_TRYWAIT);
352                                         NFSD_LOCK();
353                                         rp->rc_flag |= RC_REPMBUF;
354                                 }
355                         }
356                         rp->rc_flag &= ~RC_LOCKED;
357                         if (rp->rc_flag & RC_WANTED) {
358                                 rp->rc_flag &= ~RC_WANTED;
359                                 wakeup(rp);
360                         }
361                         return;
362                 }
363         }
364         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
365 }
366
367 /*
368  * Clean out the cache. Called when the last nfsd terminates.
369  */
370 void
371 nfsrv_cleancache(void)
372 {
373         struct nfsrvcache *rp, *nextrp;
374
375         NFSD_LOCK_ASSERT();
376
377         TAILQ_FOREACH_SAFE(rp, &nfsrvlruhead, rc_lru, nextrp) {
378                 LIST_REMOVE(rp, rc_hash);
379                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
380                 if (rp->rc_flag & RC_REPMBUF)
381                         m_freem(rp->rc_reply);
382                 if (rp->rc_flag & RC_NAM)
383                         free(rp->rc_nam, M_SONAME);
384                 free(rp, M_NFSD);
385         }
386         numnfsrvcache = 0;
387 }