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