]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/nfsserver/nfs_srvcache.c
This commit was generated by cvs2svn to compensate for changes in r149245,
[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
53 #include <netinet/in.h>
54 #include <nfs/rpcv2.h>
55 #include <nfs/nfsproto.h>
56 #include <nfsserver/nfs.h>
57 #include <nfsserver/nfsrvcache.h>
58
59 static long numnfsrvcache;
60 static long desirednfsrvcache = NFSRVCACHESIZ;
61
62 #define NFSRCHASH(xid) \
63         (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
64 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
65 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
66 static u_long nfsrvhash;
67
68 #define TRUE    1
69 #define FALSE   0
70
71 #define NETFAMILY(rp) \
72                 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
73
74 /*
75  * Static array that defines which nfs rpc's are nonidempotent
76  */
77 static const int nonidempotent[NFS_NPROCS] = {
78         FALSE,
79         FALSE,
80         TRUE,
81         FALSE,
82         FALSE,
83         FALSE,
84         FALSE,
85         TRUE,
86         TRUE,
87         TRUE,
88         TRUE,
89         TRUE,
90         TRUE,
91         TRUE,
92         TRUE,
93         TRUE,
94         FALSE,
95         FALSE,
96         FALSE,
97         FALSE,
98         FALSE,
99         FALSE,
100         FALSE,
101 };
102
103 /* True iff the rpc reply is an nfs status ONLY! */
104 static const int nfsv2_repstat[NFS_NPROCS] = {
105         FALSE,
106         FALSE,
107         FALSE,
108         FALSE,
109         FALSE,
110         FALSE,
111         FALSE,
112         FALSE,
113         FALSE,
114         FALSE,
115         TRUE,
116         TRUE,
117         TRUE,
118         TRUE,
119         FALSE,
120         TRUE,
121         FALSE,
122         FALSE,
123 };
124
125 /*
126  * Initialize the server request cache list
127  */
128 void
129 nfsrv_initcache(void)
130 {
131
132         nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
133         TAILQ_INIT(&nfsrvlruhead);
134 }
135
136 /*
137  * Look for the request in the cache
138  * If found then
139  *    return action and optionally reply
140  * else
141  *    insert it in the cache
142  *
143  * The rules are as follows:
144  * - if in progress, return DROP request
145  * - if completed within DELAY of the current time, return DROP it
146  * - if completed a longer time ago return REPLY if the reply was cached or
147  *   return DOIT
148  * Update/add new request at end of lru list
149  */
150 int
151 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
152 {
153         struct nfsrvcache *rp;
154         struct mbuf *mb;
155         struct sockaddr_in *saddr;
156         caddr_t bpos;
157         int ret;
158
159         NFSD_LOCK_ASSERT();
160
161         /*
162          * Don't cache recent requests for reliable transport protocols.
163          * (Maybe we should for the case of a reconnect, but..)
164          */
165         if (!nd->nd_nam2)
166                 return (RC_DOIT);
167 loop:
168         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
169             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
170                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
171                         NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
172                         if ((rp->rc_flag & RC_LOCKED) != 0) {
173                                 rp->rc_flag |= RC_WANTED;
174                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
175                                     "nfsrc", 0);
176                                 goto loop;
177                         }
178                         rp->rc_flag |= RC_LOCKED;
179                         /* If not at end of LRU chain, move it there */
180                         if (TAILQ_NEXT(rp, rc_lru)) {
181                                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
182                                 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
183                         }
184                         if (rp->rc_state == RC_UNUSED)
185                                 panic("nfsrv cache");
186                         if (rp->rc_state == RC_INPROG) {
187                                 nfsrvstats.srvcache_inproghits++;
188                                 ret = RC_DROPIT;
189                         } else if (rp->rc_flag & RC_REPSTATUS) {
190                                 nfsrvstats.srvcache_nonidemdonehits++;
191                                 *repp = nfs_rephead(0, nd, rp->rc_status,
192                                     &mb, &bpos);
193                                 ret = RC_REPLY;
194                         } else if (rp->rc_flag & RC_REPMBUF) {
195                                 nfsrvstats.srvcache_nonidemdonehits++;
196                                 NFSD_UNLOCK();
197                                 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
198                                                 M_TRYWAIT);
199                                 NFSD_LOCK();
200                                 ret = RC_REPLY;
201                         } else {
202                                 nfsrvstats.srvcache_idemdonehits++;
203                                 rp->rc_state = RC_INPROG;
204                                 ret = RC_DOIT;
205                         }
206                         rp->rc_flag &= ~RC_LOCKED;
207                         if (rp->rc_flag & RC_WANTED) {
208                                 rp->rc_flag &= ~RC_WANTED;
209                                 wakeup(rp);
210                         }
211                         return (ret);
212                 }
213         }
214         nfsrvstats.srvcache_misses++;
215         NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
216         if (numnfsrvcache < desirednfsrvcache) {
217                 NFSD_UNLOCK();
218                 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
219                     M_NFSD, M_WAITOK | M_ZERO);
220                 NFSD_LOCK();
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) msleep(rp, &nfsd_mtx, PZERO-1, "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_INET6:  */
249 /*      case AF_ISO:    */
250         default:
251                 /*
252                  * XXXRW: Seems like we should only set RC_NAM if we
253                  * actually manage to set rc_nam to something non-NULL.
254                  */
255                 rp->rc_flag |= RC_NAM;
256                 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
257                 break;
258         };
259         rp->rc_proc = nd->nd_procnum;
260         LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
261         rp->rc_flag &= ~RC_LOCKED;
262         if (rp->rc_flag & RC_WANTED) {
263                 rp->rc_flag &= ~RC_WANTED;
264                 wakeup(rp);
265         }
266         return (RC_DOIT);
267 }
268
269 /*
270  * Update a request cache entry after the rpc has been done
271  */
272 void
273 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
274 {
275         struct nfsrvcache *rp;
276
277         NFSD_LOCK_ASSERT();
278
279         if (!nd->nd_nam2)
280                 return;
281 loop:
282         LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
283             if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
284                 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
285                         NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
286                         if ((rp->rc_flag & RC_LOCKED) != 0) {
287                                 rp->rc_flag |= RC_WANTED;
288                                 (void) msleep(rp, &nfsd_mtx, PZERO-1,
289                                     "nfsrc", 0);
290                                 goto loop;
291                         }
292                         rp->rc_flag |= RC_LOCKED;
293                         if (rp->rc_state == RC_DONE) {
294                                 /*
295                                  * This can occur if the cache is too small.
296                                  * Retransmits of the same request aren't
297                                  * dropped so we may see the operation
298                                  * complete more then once.
299                                  */
300                                 if (rp->rc_flag & RC_REPMBUF) {
301                                         m_freem(rp->rc_reply);
302                                         rp->rc_flag &= ~RC_REPMBUF;
303                                 }
304                         }
305                         rp->rc_state = RC_DONE;
306                         /*
307                          * If we have a valid reply update status and save
308                          * the reply for non-idempotent rpc's.
309                          */
310                         if (repvalid && nonidempotent[nd->nd_procnum]) {
311                                 if ((nd->nd_flag & ND_NFSV3) == 0 &&
312                                     nfsv2_repstat[
313                                     nfsrvv2_procid[nd->nd_procnum]]) {
314                                         rp->rc_status = nd->nd_repstat;
315                                         rp->rc_flag |= RC_REPSTATUS;
316                                 } else {
317                                         NFSD_UNLOCK();
318                                         rp->rc_reply = m_copym(repmbuf,
319                                                 0, M_COPYALL, M_TRYWAIT);
320                                         NFSD_LOCK();
321                                         rp->rc_flag |= RC_REPMBUF;
322                                 }
323                         }
324                         rp->rc_flag &= ~RC_LOCKED;
325                         if (rp->rc_flag & RC_WANTED) {
326                                 rp->rc_flag &= ~RC_WANTED;
327                                 wakeup(rp);
328                         }
329                         return;
330                 }
331         }
332         NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
333 }
334
335 /*
336  * Clean out the cache. Called when the last nfsd terminates.
337  */
338 void
339 nfsrv_cleancache(void)
340 {
341         struct nfsrvcache *rp, *nextrp;
342
343         NFSD_LOCK_ASSERT();
344
345         for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
346                 nextrp = TAILQ_NEXT(rp, rc_lru);
347                 LIST_REMOVE(rp, rc_hash);
348                 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
349                 if (rp->rc_flag & RC_REPMBUF)
350                         m_freem(rp->rc_reply);
351                 if (rp->rc_flag & RC_NAM)
352                         free(rp->rc_nam, M_SONAME);
353                 free(rp, M_NFSD);
354         }
355         numnfsrvcache = 0;
356 }