nfs_srvcache.c revision 1.1.1.2 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. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)nfs_srvcache.c 8.1 (Berkeley) 6/10/93
37 */
38
39 /*
40 * Reference: Chet Juszczak, "Improving the Performance and Correctness
41 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
42 * pages 53-63. San Diego, February 1989.
43 */
44 #include <sys/param.h>
45 #include <sys/vnode.h>
46 #include <sys/mount.h>
47 #include <sys/kernel.h>
48 #include <sys/systm.h>
49 #include <sys/proc.h>
50 #include <sys/mbuf.h>
51 #include <sys/malloc.h>
52 #include <sys/socket.h>
53 #include <sys/socketvar.h>
54
55 #include <netinet/in.h>
56 #ifdef ISO
57 #include <netiso/iso.h>
58 #endif
59 #include <nfs/nfsm_subs.h>
60 #include <nfs/rpcv2.h>
61 #include <nfs/nfsv2.h>
62 #include <nfs/nfs.h>
63 #include <nfs/nfsrvcache.h>
64 #include <nfs/nqnfs.h>
65
66 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
67
68 #define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & rheadhash)
69 static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
70 static struct nfsrvcache **rheadhtbl;
71 static u_long rheadhash;
72
73 #define TRUE 1
74 #define FALSE 0
75
76 #define NETFAMILY(rp) \
77 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
78
79 /*
80 * Static array that defines which nfs rpc's are nonidempotent
81 */
82 int nonidempotent[NFS_NPROCS] = {
83 FALSE,
84 FALSE,
85 TRUE,
86 FALSE,
87 FALSE,
88 FALSE,
89 FALSE,
90 FALSE,
91 TRUE,
92 TRUE,
93 TRUE,
94 TRUE,
95 TRUE,
96 TRUE,
97 TRUE,
98 TRUE,
99 FALSE,
100 FALSE,
101 FALSE,
102 FALSE,
103 FALSE,
104 FALSE,
105 FALSE,
106 };
107
108 /* True iff the rpc reply is an nfs status ONLY! */
109 static int repliesstatus[NFS_NPROCS] = {
110 FALSE,
111 FALSE,
112 FALSE,
113 FALSE,
114 FALSE,
115 FALSE,
116 FALSE,
117 FALSE,
118 FALSE,
119 FALSE,
120 TRUE,
121 TRUE,
122 TRUE,
123 TRUE,
124 FALSE,
125 TRUE,
126 FALSE,
127 FALSE,
128 FALSE,
129 FALSE,
130 FALSE,
131 FALSE,
132 TRUE,
133 };
134
135 /*
136 * Initialize the server request cache list
137 */
138 nfsrv_initcache()
139 {
140
141 rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
142 }
143
144 /*
145 * Look for the request in the cache
146 * If found then
147 * return action and optionally reply
148 * else
149 * insert it in the cache
150 *
151 * The rules are as follows:
152 * - if in progress, return DROP request
153 * - if completed within DELAY of the current time, return DROP it
154 * - if completed a longer time ago return REPLY if the reply was cached or
155 * return DOIT
156 * Update/add new request at end of lru list
157 */
158 nfsrv_getcache(nam, nd, repp)
159 struct mbuf *nam;
160 register struct nfsd *nd;
161 struct mbuf **repp;
162 {
163 register struct nfsrvcache *rp, *rq, **rpp;
164 struct mbuf *mb;
165 struct sockaddr_in *saddr;
166 caddr_t bpos;
167 int ret;
168
169 if (nd->nd_nqlflag != NQL_NOVAL)
170 return (RC_DOIT);
171 rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
172 loop:
173 for (rp = *rpp; rp; rp = rp->rc_forw) {
174 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
175 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
176 if ((rp->rc_flag & RC_LOCKED) != 0) {
177 rp->rc_flag |= RC_WANTED;
178 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
179 goto loop;
180 }
181 rp->rc_flag |= RC_LOCKED;
182 /* If not at end of LRU chain, move it there */
183 if (rp->rc_next) {
184 /* remove from LRU chain */
185 *rp->rc_prev = rp->rc_next;
186 rp->rc_next->rc_prev = rp->rc_prev;
187 /* and replace at end of it */
188 rp->rc_next = NULL;
189 rp->rc_prev = nfsrvlrutail;
190 *nfsrvlrutail = rp;
191 nfsrvlrutail = &rp->rc_next;
192 }
193 if (rp->rc_state == RC_UNUSED)
194 panic("nfsrv cache");
195 if (rp->rc_state == RC_INPROG) {
196 nfsstats.srvcache_inproghits++;
197 ret = RC_DROPIT;
198 } else if (rp->rc_flag & RC_REPSTATUS) {
199 nfsstats.srvcache_nonidemdonehits++;
200 nfs_rephead(0, nd, rp->rc_status,
201 0, (u_quad_t *)0, repp, &mb, &bpos);
202 ret = RC_REPLY;
203 } else if (rp->rc_flag & RC_REPMBUF) {
204 nfsstats.srvcache_nonidemdonehits++;
205 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
206 M_WAIT);
207 ret = RC_REPLY;
208 } else {
209 nfsstats.srvcache_idemdonehits++;
210 rp->rc_state = RC_INPROG;
211 ret = RC_DOIT;
212 }
213 rp->rc_flag &= ~RC_LOCKED;
214 if (rp->rc_flag & RC_WANTED) {
215 rp->rc_flag &= ~RC_WANTED;
216 wakeup((caddr_t)rp);
217 }
218 return (ret);
219 }
220 }
221 nfsstats.srvcache_misses++;
222 if (numnfsrvcache < desirednfsrvcache) {
223 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
224 M_NFSD, M_WAITOK);
225 bzero((char *)rp, sizeof *rp);
226 numnfsrvcache++;
227 rp->rc_flag = RC_LOCKED;
228 } else {
229 rp = nfsrvlruhead;
230 while ((rp->rc_flag & RC_LOCKED) != 0) {
231 rp->rc_flag |= RC_WANTED;
232 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
233 rp = nfsrvlruhead;
234 }
235 rp->rc_flag |= RC_LOCKED;
236 /* remove from hash chain */
237 if (rq = rp->rc_forw)
238 rq->rc_back = rp->rc_back;
239 *rp->rc_back = rq;
240 /* remove from LRU chain */
241 *rp->rc_prev = rp->rc_next;
242 rp->rc_next->rc_prev = rp->rc_prev;
243 if (rp->rc_flag & RC_REPMBUF)
244 m_freem(rp->rc_reply);
245 if (rp->rc_flag & RC_NAM)
246 MFREE(rp->rc_nam, mb);
247 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
248 }
249 /* place at end of LRU list */
250 rp->rc_next = NULL;
251 rp->rc_prev = nfsrvlrutail;
252 *nfsrvlrutail = rp;
253 nfsrvlrutail = &rp->rc_next;
254 rp->rc_state = RC_INPROG;
255 rp->rc_xid = nd->nd_retxid;
256 saddr = mtod(nam, struct sockaddr_in *);
257 switch (saddr->sin_family) {
258 case AF_INET:
259 rp->rc_flag |= RC_INETADDR;
260 rp->rc_inetaddr = saddr->sin_addr.s_addr;
261 break;
262 case AF_ISO:
263 default:
264 rp->rc_flag |= RC_NAM;
265 rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
266 break;
267 };
268 rp->rc_proc = nd->nd_procnum;
269 /* insert into hash chain */
270 if (rq = *rpp)
271 rq->rc_back = &rp->rc_forw;
272 rp->rc_forw = rq;
273 rp->rc_back = rpp;
274 *rpp = rp;
275 rp->rc_flag &= ~RC_LOCKED;
276 if (rp->rc_flag & RC_WANTED) {
277 rp->rc_flag &= ~RC_WANTED;
278 wakeup((caddr_t)rp);
279 }
280 return (RC_DOIT);
281 }
282
283 /*
284 * Update a request cache entry after the rpc has been done
285 */
286 void
287 nfsrv_updatecache(nam, nd, repvalid, repmbuf)
288 struct mbuf *nam;
289 register struct nfsd *nd;
290 int repvalid;
291 struct mbuf *repmbuf;
292 {
293 register struct nfsrvcache *rp;
294
295 if (nd->nd_nqlflag != NQL_NOVAL)
296 return;
297 loop:
298 for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
299 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
300 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
301 if ((rp->rc_flag & RC_LOCKED) != 0) {
302 rp->rc_flag |= RC_WANTED;
303 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
304 goto loop;
305 }
306 rp->rc_flag |= RC_LOCKED;
307 rp->rc_state = RC_DONE;
308 /*
309 * If we have a valid reply update status and save
310 * the reply for non-idempotent rpc's.
311 */
312 if (repvalid && nonidempotent[nd->nd_procnum]) {
313 if (repliesstatus[nd->nd_procnum]) {
314 rp->rc_status = nd->nd_repstat;
315 rp->rc_flag |= RC_REPSTATUS;
316 } else {
317 rp->rc_reply = m_copym(repmbuf,
318 0, M_COPYALL, M_WAIT);
319 rp->rc_flag |= RC_REPMBUF;
320 }
321 }
322 rp->rc_flag &= ~RC_LOCKED;
323 if (rp->rc_flag & RC_WANTED) {
324 rp->rc_flag &= ~RC_WANTED;
325 wakeup((caddr_t)rp);
326 }
327 return;
328 }
329 }
330 }
331
332 /*
333 * Clean out the cache. Called when the last nfsd terminates.
334 */
335 void
336 nfsrv_cleancache()
337 {
338 register struct nfsrvcache *rp, *nextrp;
339
340 for (rp = nfsrvlruhead; rp; rp = nextrp) {
341 nextrp = rp->rc_next;
342 free(rp, M_NFSD);
343 }
344 bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
345 nfsrvlruhead = NULL;
346 nfsrvlrutail = &nfsrvlruhead;
347 numnfsrvcache = 0;
348 }
349