Commit | Line | Data |
---|---|---|
984263bc MD |
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. | |
dc71b7ab | 16 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
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 | * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $ | |
34 | */ | |
35 | ||
36 | /* | |
37 | * Reference: Chet Juszczak, "Improving the Performance and Correctness | |
38 | * of an NFS Server", in Proc. Winter 1989 USENIX Conference, | |
39 | * pages 53-63. San Diego, February 1989. | |
40 | */ | |
41 | #include <sys/param.h> | |
42 | #include <sys/malloc.h> | |
43 | #include <sys/mount.h> | |
44 | #include <sys/systm.h> | |
45 | #include <sys/mbuf.h> | |
46 | #include <sys/socket.h> | |
47 | #include <sys/socketvar.h> /* for dup_sockaddr */ | |
48 | ||
49 | #include <netinet/in.h> | |
1f2de5d4 MD |
50 | #include "rpcv2.h" |
51 | #include "nfsproto.h" | |
52 | #include "nfs.h" | |
53 | #include "nfsrvcache.h" | |
984263bc MD |
54 | |
55 | #ifndef NFS_NOSERVER | |
984263bc | 56 | static long numnfsrvcache; |
a084b5c3 | 57 | static long desirednfsrvcache; |
984263bc MD |
58 | |
59 | #define NFSRCHASH(xid) \ | |
60 | (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) | |
61 | static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; | |
62 | static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; | |
63 | static u_long nfsrvhash; | |
64 | ||
65 | #define TRUE 1 | |
66 | #define FALSE 0 | |
67 | ||
a3c18566 | 68 | struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token); |
5e6f1ca5 | 69 | |
984263bc MD |
70 | /* |
71 | * Static array that defines which nfs rpc's are nonidempotent | |
72 | */ | |
73 | static int nonidempotent[NFS_NPROCS] = { | |
74 | FALSE, | |
75 | FALSE, | |
76 | TRUE, | |
77 | FALSE, | |
78 | FALSE, | |
79 | FALSE, | |
80 | FALSE, | |
81 | TRUE, | |
82 | TRUE, | |
83 | TRUE, | |
84 | TRUE, | |
85 | TRUE, | |
86 | TRUE, | |
87 | TRUE, | |
88 | TRUE, | |
89 | TRUE, | |
90 | FALSE, | |
91 | FALSE, | |
92 | FALSE, | |
93 | FALSE, | |
94 | FALSE, | |
95 | FALSE, | |
96 | FALSE, | |
97 | FALSE, | |
98 | FALSE, | |
99 | FALSE, | |
100 | }; | |
101 | ||
102 | /* True iff the rpc reply is an nfs status ONLY! */ | |
103 | static int nfsv2_repstat[NFS_NPROCS] = { | |
104 | FALSE, | |
105 | FALSE, | |
106 | FALSE, | |
107 | FALSE, | |
108 | FALSE, | |
109 | FALSE, | |
110 | FALSE, | |
111 | FALSE, | |
112 | FALSE, | |
113 | FALSE, | |
114 | TRUE, | |
115 | TRUE, | |
116 | TRUE, | |
117 | TRUE, | |
118 | FALSE, | |
119 | TRUE, | |
120 | FALSE, | |
121 | FALSE, | |
122 | }; | |
123 | ||
a084b5c3 VS |
124 | /* |
125 | * Size the NFS server's duplicate request cache at 1/2 the nmbclusters, | |
126 | * within a (64, 2048) range. This is to prevent all mbuf clusters being | |
127 | * tied up in the NFS dupreq cache for small values of nmbclusters. | |
128 | */ | |
129 | static void | |
130 | nfsrvcache_size_change(void) | |
131 | { | |
132 | desirednfsrvcache = nmbclusters / 2; | |
133 | desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE); | |
134 | desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE); | |
135 | } | |
136 | ||
984263bc MD |
137 | /* |
138 | * Initialize the server request cache list | |
139 | */ | |
140 | void | |
e851b29e | 141 | nfsrv_initcache(void) |
984263bc | 142 | { |
a084b5c3 | 143 | nfsrvcache_size_change(); |
984263bc MD |
144 | nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash); |
145 | TAILQ_INIT(&nfsrvlruhead); | |
146 | } | |
147 | ||
148 | /* | |
149 | * Look for the request in the cache | |
150 | * If found then | |
151 | * return action and optionally reply | |
152 | * else | |
153 | * insert it in the cache | |
154 | * | |
155 | * The rules are as follows: | |
156 | * - if in progress, return DROP request | |
157 | * - if completed within DELAY of the current time, return DROP it | |
158 | * - if completed a longer time ago return REPLY if the reply was cached or | |
159 | * return DOIT | |
160 | * Update/add new request at end of lru list | |
161 | */ | |
162 | int | |
e851b29e CP |
163 | nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, |
164 | struct mbuf **repp) | |
984263bc | 165 | { |
40393ded | 166 | struct nfsrvcache *rp; |
984263bc MD |
167 | struct mbuf *mb; |
168 | struct sockaddr_in *saddr; | |
169 | caddr_t bpos; | |
170 | int ret; | |
171 | ||
172 | /* | |
173 | * Don't cache recent requests for reliable transport protocols. | |
174 | * (Maybe we should for the case of a reconnect, but..) | |
175 | */ | |
176 | if (!nd->nd_nam2) | |
177 | return (RC_DOIT); | |
5e6f1ca5 MD |
178 | |
179 | lwkt_gettoken(&srvcache_token); | |
984263bc | 180 | loop: |
4090d6ff | 181 | for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; |
984263bc MD |
182 | rp = rp->rc_hash.le_next) { |
183 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && | |
6ec8f7f2 | 184 | netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { |
984263bc MD |
185 | NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff)); |
186 | if ((rp->rc_flag & RC_LOCKED) != 0) { | |
187 | rp->rc_flag |= RC_WANTED; | |
96f9dac1 | 188 | tsleep((caddr_t)rp, 0, "nfsrc", 0); |
984263bc MD |
189 | goto loop; |
190 | } | |
191 | rp->rc_flag |= RC_LOCKED; | |
192 | /* If not at end of LRU chain, move it there */ | |
ecd80f47 | 193 | if (TAILQ_NEXT(rp, rc_lru) != NULL) { |
984263bc MD |
194 | TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); |
195 | TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); | |
196 | } | |
197 | if (rp->rc_state == RC_UNUSED) | |
198 | panic("nfsrv cache"); | |
199 | if (rp->rc_state == RC_INPROG) { | |
200 | nfsstats.srvcache_inproghits++; | |
201 | ret = RC_DROPIT; | |
202 | } else if (rp->rc_flag & RC_REPSTATUS) { | |
203 | nfsstats.srvcache_nonidemdonehits++; | |
204 | nfs_rephead(0, nd, slp, rp->rc_status, | |
e07fef60 | 205 | repp, &mb, &bpos); |
984263bc MD |
206 | ret = RC_REPLY; |
207 | } else if (rp->rc_flag & RC_REPMBUF) { | |
208 | nfsstats.srvcache_nonidemdonehits++; | |
209 | *repp = m_copym(rp->rc_reply, 0, M_COPYALL, | |
b5523eac | 210 | M_WAITOK); |
984263bc MD |
211 | ret = RC_REPLY; |
212 | } else { | |
213 | nfsstats.srvcache_idemdonehits++; | |
214 | rp->rc_state = RC_INPROG; | |
215 | ret = RC_DOIT; | |
216 | } | |
217 | rp->rc_flag &= ~RC_LOCKED; | |
218 | if (rp->rc_flag & RC_WANTED) { | |
219 | rp->rc_flag &= ~RC_WANTED; | |
220 | wakeup((caddr_t)rp); | |
221 | } | |
5e6f1ca5 | 222 | lwkt_reltoken(&srvcache_token); |
984263bc MD |
223 | return (ret); |
224 | } | |
225 | } | |
5e6f1ca5 | 226 | |
984263bc MD |
227 | nfsstats.srvcache_misses++; |
228 | NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff)); | |
229 | if (numnfsrvcache < desirednfsrvcache) { | |
96f9dac1 | 230 | rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO); |
984263bc MD |
231 | numnfsrvcache++; |
232 | rp->rc_flag = RC_LOCKED; | |
233 | } else { | |
ecd80f47 | 234 | rp = TAILQ_FIRST(&nfsrvlruhead); |
984263bc MD |
235 | while ((rp->rc_flag & RC_LOCKED) != 0) { |
236 | rp->rc_flag |= RC_WANTED; | |
96f9dac1 | 237 | tsleep((caddr_t)rp, 0, "nfsrc", 0); |
ecd80f47 | 238 | rp = TAILQ_FIRST(&nfsrvlruhead); |
984263bc MD |
239 | } |
240 | rp->rc_flag |= RC_LOCKED; | |
241 | LIST_REMOVE(rp, rc_hash); | |
242 | TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); | |
96f9dac1 | 243 | if (rp->rc_flag & RC_REPMBUF) { |
984263bc | 244 | m_freem(rp->rc_reply); |
96f9dac1 MD |
245 | rp->rc_reply = NULL; |
246 | rp->rc_flag &= ~RC_REPMBUF; | |
247 | } | |
248 | if (rp->rc_flag & RC_NAM) { | |
884717e1 | 249 | kfree(rp->rc_nam, M_SONAME); |
96f9dac1 MD |
250 | rp->rc_nam = NULL; |
251 | rp->rc_flag &= ~RC_NAM; | |
252 | } | |
984263bc MD |
253 | } |
254 | TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); | |
5e6f1ca5 | 255 | |
984263bc MD |
256 | rp->rc_state = RC_INPROG; |
257 | rp->rc_xid = nd->nd_retxid; | |
258 | saddr = (struct sockaddr_in *)nd->nd_nam; | |
259 | switch (saddr->sin_family) { | |
260 | case AF_INET: | |
261 | rp->rc_flag |= RC_INETADDR; | |
262 | rp->rc_inetaddr = saddr->sin_addr.s_addr; | |
263 | break; | |
984263bc MD |
264 | default: |
265 | rp->rc_flag |= RC_NAM; | |
cfa2ba21 | 266 | rp->rc_nam = dup_sockaddr(nd->nd_nam); |
984263bc | 267 | break; |
0fdb7d01 | 268 | } |
984263bc MD |
269 | rp->rc_proc = nd->nd_procnum; |
270 | LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); | |
271 | rp->rc_flag &= ~RC_LOCKED; | |
272 | if (rp->rc_flag & RC_WANTED) { | |
273 | rp->rc_flag &= ~RC_WANTED; | |
274 | wakeup((caddr_t)rp); | |
275 | } | |
5e6f1ca5 MD |
276 | lwkt_reltoken(&srvcache_token); |
277 | ||
984263bc MD |
278 | return (RC_DOIT); |
279 | } | |
280 | ||
281 | /* | |
282 | * Update a request cache entry after the rpc has been done | |
283 | */ | |
284 | void | |
e851b29e | 285 | nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf) |
984263bc | 286 | { |
40393ded | 287 | struct nfsrvcache *rp; |
984263bc MD |
288 | |
289 | if (!nd->nd_nam2) | |
290 | return; | |
5e6f1ca5 MD |
291 | |
292 | lwkt_gettoken(&srvcache_token); | |
984263bc | 293 | loop: |
4090d6ff | 294 | for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; |
984263bc MD |
295 | rp = rp->rc_hash.le_next) { |
296 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && | |
6ec8f7f2 | 297 | netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { |
984263bc MD |
298 | NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff)); |
299 | if ((rp->rc_flag & RC_LOCKED) != 0) { | |
300 | rp->rc_flag |= RC_WANTED; | |
96f9dac1 | 301 | tsleep((caddr_t)rp, 0, "nfsrc", 0); |
984263bc MD |
302 | goto loop; |
303 | } | |
304 | rp->rc_flag |= RC_LOCKED; | |
305 | if (rp->rc_state == RC_DONE) { | |
306 | /* | |
307 | * This can occur if the cache is too small. | |
308 | * Retransmits of the same request aren't | |
309 | * dropped so we may see the operation | |
310 | * complete more then once. | |
311 | */ | |
312 | if (rp->rc_flag & RC_REPMBUF) { | |
313 | m_freem(rp->rc_reply); | |
96f9dac1 | 314 | rp->rc_reply = NULL; |
984263bc MD |
315 | rp->rc_flag &= ~RC_REPMBUF; |
316 | } | |
317 | } | |
318 | rp->rc_state = RC_DONE; | |
96f9dac1 | 319 | |
984263bc MD |
320 | /* |
321 | * If we have a valid reply update status and save | |
322 | * the reply for non-idempotent rpc's. | |
323 | */ | |
324 | if (repvalid && nonidempotent[nd->nd_procnum]) { | |
325 | if ((nd->nd_flag & ND_NFSV3) == 0 && | |
326 | nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { | |
327 | rp->rc_status = nd->nd_repstat; | |
328 | rp->rc_flag |= RC_REPSTATUS; | |
329 | } else { | |
96f9dac1 MD |
330 | if (rp->rc_flag & RC_REPMBUF) { |
331 | m_freem(rp->rc_reply); | |
332 | rp->rc_reply = NULL; | |
333 | rp->rc_flag &= ~RC_REPMBUF; | |
334 | } | |
335 | rp->rc_reply = m_copym(repmbuf, 0, | |
b5523eac | 336 | M_COPYALL, M_WAITOK); |
984263bc MD |
337 | rp->rc_flag |= RC_REPMBUF; |
338 | } | |
339 | } | |
340 | rp->rc_flag &= ~RC_LOCKED; | |
341 | if (rp->rc_flag & RC_WANTED) { | |
342 | rp->rc_flag &= ~RC_WANTED; | |
343 | wakeup((caddr_t)rp); | |
344 | } | |
5e6f1ca5 | 345 | break; |
984263bc MD |
346 | } |
347 | } | |
5e6f1ca5 | 348 | lwkt_reltoken(&srvcache_token); |
984263bc MD |
349 | NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff)); |
350 | } | |
351 | ||
352 | /* | |
353 | * Clean out the cache. Called when the last nfsd terminates. | |
354 | */ | |
355 | void | |
e851b29e | 356 | nfsrv_cleancache(void) |
984263bc | 357 | { |
96f9dac1 | 358 | struct nfsrvcache *rp; |
984263bc | 359 | |
5e6f1ca5 | 360 | lwkt_gettoken(&srvcache_token); |
96f9dac1 MD |
361 | while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) { |
362 | if (rp->rc_flag & RC_LOCKED) { | |
363 | rp->rc_flag |= RC_WANTED; | |
364 | tsleep((caddr_t)rp, 0, "nfsrc", 0); | |
365 | continue; | |
366 | } | |
984263bc MD |
367 | LIST_REMOVE(rp, rc_hash); |
368 | TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); | |
96f9dac1 | 369 | if (rp->rc_flag & RC_REPMBUF) { |
984263bc | 370 | m_freem(rp->rc_reply); |
96f9dac1 MD |
371 | rp->rc_reply = NULL; |
372 | rp->rc_flag &= ~RC_REPMBUF; | |
373 | } | |
374 | if (rp->rc_flag & RC_NAM) { | |
efda3bd0 | 375 | kfree(rp->rc_nam, M_SONAME); |
96f9dac1 MD |
376 | rp->rc_nam = NULL; |
377 | rp->rc_flag &= ~RC_NAM; | |
378 | } | |
efda3bd0 | 379 | kfree(rp, M_NFSD); |
984263bc MD |
380 | } |
381 | numnfsrvcache = 0; | |
5e6f1ca5 | 382 | lwkt_reltoken(&srvcache_token); |
984263bc MD |
383 | } |
384 | ||
385 | #endif /* NFS_NOSERVER */ |