Commit | Line | Data |
---|---|---|
95f0eacd KM |
1 | /* |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Rick Macklem at The University of Guelph. | |
7 | * | |
dbf0c423 | 8 | * %sccs.include.redist.c% |
95f0eacd | 9 | * |
d615f2fe | 10 | * @(#)nfs_srvcache.c 7.15 (Berkeley) %G% |
b54e05cc KM |
11 | */ |
12 | ||
13 | /* | |
14 | * Reference: Chet Juszczak, "Improving the Performance and Correctness | |
2c5b44a2 KM |
15 | * of an NFS Server", in Proc. Winter 1989 USENIX Conference, |
16 | * pages 53-63. San Diego, February 1989. | |
95f0eacd | 17 | */ |
d615f2fe KM |
18 | #include <sys/param.h> |
19 | #include <sys/vnode.h> | |
20 | #include <sys/mount.h> | |
21 | #include <sys/kernel.h> | |
22 | #include <sys/systm.h> | |
23 | #include <sys/proc.h> | |
24 | #include <sys/mbuf.h> | |
25 | #include <sys/socket.h> | |
26 | #include <sys/socketvar.h> | |
27 | #include <netinet/in.h> | |
2c5b44a2 | 28 | #ifdef ISO |
d615f2fe | 29 | #include <netiso/iso.h> |
2c5b44a2 | 30 | #endif |
d615f2fe KM |
31 | #include <nfs/nfsm_subs.h> |
32 | #include <nfs/rpcv2.h> | |
33 | #include <nfs/nfsv2.h> | |
34 | #include <nfs/nfs.h> | |
35 | #include <nfs/nfsrvcache.h> | |
36 | #include <nfs/nqnfs.h> | |
95f0eacd KM |
37 | |
38 | #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) | |
39 | #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) | |
40 | #else | |
41 | #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) | |
42 | #endif | |
43 | ||
44 | union rhead { | |
45 | union rhead *rh_head[2]; | |
46 | struct nfsrvcache *rh_chain[2]; | |
47 | } rhead[NFSRCHSZ]; | |
48 | ||
49 | static struct nfsrvcache nfsrvcachehead; | |
50 | static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; | |
51 | ||
52 | #define TRUE 1 | |
53 | #define FALSE 0 | |
54 | ||
2c5b44a2 KM |
55 | #define NETFAMILY(rp) \ |
56 | (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) | |
57 | ||
95f0eacd KM |
58 | /* |
59 | * Static array that defines which nfs rpc's are nonidempotent | |
60 | */ | |
cdf8c1c6 | 61 | int nonidempotent[NFS_NPROCS] = { |
95f0eacd KM |
62 | FALSE, |
63 | FALSE, | |
64 | TRUE, | |
65 | FALSE, | |
66 | FALSE, | |
67 | FALSE, | |
68 | FALSE, | |
69 | FALSE, | |
70 | TRUE, | |
71 | TRUE, | |
72 | TRUE, | |
73 | TRUE, | |
74 | TRUE, | |
75 | TRUE, | |
76 | TRUE, | |
77 | TRUE, | |
78 | FALSE, | |
79 | FALSE, | |
2c5b44a2 KM |
80 | FALSE, |
81 | FALSE, | |
82 | FALSE, | |
83 | FALSE, | |
95f0eacd KM |
84 | }; |
85 | ||
86 | /* True iff the rpc reply is an nfs status ONLY! */ | |
87 | static int repliesstatus[NFS_NPROCS] = { | |
88 | FALSE, | |
89 | FALSE, | |
90 | FALSE, | |
91 | FALSE, | |
92 | FALSE, | |
93 | FALSE, | |
94 | FALSE, | |
95 | FALSE, | |
96 | FALSE, | |
97 | FALSE, | |
98 | TRUE, | |
99 | TRUE, | |
100 | TRUE, | |
101 | TRUE, | |
102 | FALSE, | |
103 | TRUE, | |
104 | FALSE, | |
105 | FALSE, | |
2c5b44a2 KM |
106 | FALSE, |
107 | FALSE, | |
108 | FALSE, | |
109 | FALSE, | |
95f0eacd KM |
110 | }; |
111 | ||
112 | /* | |
113 | * Initialize the server request cache list | |
114 | */ | |
115 | nfsrv_initcache() | |
116 | { | |
117 | register int i; | |
118 | register struct nfsrvcache *rp = nfsrvcache; | |
119 | register struct nfsrvcache *hp = &nfsrvcachehead; | |
120 | register union rhead *rh = rhead; | |
121 | ||
122 | for (i = NFSRCHSZ; --i >= 0; rh++) { | |
123 | rh->rh_head[0] = rh; | |
124 | rh->rh_head[1] = rh; | |
125 | } | |
126 | hp->rc_next = hp->rc_prev = hp; | |
127 | for (i = NFSRVCACHESIZ; i-- > 0; ) { | |
128 | rp->rc_state = RC_UNUSED; | |
129 | rp->rc_flag = 0; | |
130 | rp->rc_forw = rp; | |
131 | rp->rc_back = rp; | |
132 | rp->rc_next = hp->rc_next; | |
133 | hp->rc_next->rc_prev = rp; | |
134 | rp->rc_prev = hp; | |
135 | hp->rc_next = rp; | |
136 | rp++; | |
137 | } | |
138 | } | |
139 | ||
140 | /* | |
141 | * Look for the request in the cache | |
142 | * If found then | |
143 | * return action and optionally reply | |
144 | * else | |
145 | * insert it in the cache | |
146 | * | |
147 | * The rules are as follows: | |
148 | * - if in progress, return DROP request | |
149 | * - if completed within DELAY of the current time, return DROP it | |
150 | * - if completed a longer time ago return REPLY if the reply was cached or | |
151 | * return DOIT | |
152 | * Update/add new request at end of lru list | |
153 | */ | |
2c5b44a2 | 154 | nfsrv_getcache(nam, nd, repp) |
95f0eacd | 155 | struct mbuf *nam; |
2c5b44a2 | 156 | register struct nfsd *nd; |
95f0eacd KM |
157 | struct mbuf **repp; |
158 | { | |
159 | register struct nfsrvcache *rp; | |
160 | register union rhead *rh; | |
95f0eacd | 161 | struct mbuf *mb; |
2c5b44a2 | 162 | struct sockaddr_in *saddr; |
95f0eacd KM |
163 | caddr_t bpos; |
164 | int ret; | |
165 | ||
2c5b44a2 KM |
166 | if (nd->nd_nqlflag != NQL_NOVAL) |
167 | return (RC_DOIT); | |
168 | rh = &rhead[NFSRCHASH(nd->nd_retxid)]; | |
95f0eacd KM |
169 | loop: |
170 | for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { | |
2c5b44a2 | 171 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && |
4197fd4a | 172 | netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { |
95f0eacd KM |
173 | if ((rp->rc_flag & RC_LOCKED) != 0) { |
174 | rp->rc_flag |= RC_WANTED; | |
170bfd05 | 175 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); |
95f0eacd KM |
176 | goto loop; |
177 | } | |
178 | rp->rc_flag |= RC_LOCKED; | |
2c5b44a2 KM |
179 | if (rp->rc_prev != &nfsrvcachehead) { |
180 | rp->rc_next->rc_prev = rp->rc_prev; | |
181 | rp->rc_prev->rc_next = rp->rc_next; | |
182 | rp->rc_next = nfsrvcachehead.rc_next; | |
183 | nfsrvcachehead.rc_next = rp; | |
184 | rp->rc_prev = &nfsrvcachehead; | |
185 | rp->rc_next->rc_prev = rp; | |
186 | } | |
95f0eacd KM |
187 | if (rp->rc_state == RC_UNUSED) |
188 | panic("nfsrv cache"); | |
189 | if (rp->rc_state == RC_INPROG || | |
190 | (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { | |
191 | nfsstats.srvcache_inproghits++; | |
192 | ret = RC_DROPIT; | |
193 | } else if (rp->rc_flag & RC_REPSTATUS) { | |
194 | nfsstats.srvcache_idemdonehits++; | |
2c5b44a2 KM |
195 | nfs_rephead(0, nd, rp->rc_status, |
196 | 0, (u_quad_t *)0, repp, &mb, &bpos); | |
95f0eacd KM |
197 | rp->rc_timestamp = time.tv_sec; |
198 | ret = RC_REPLY; | |
199 | } else if (rp->rc_flag & RC_REPMBUF) { | |
200 | nfsstats.srvcache_idemdonehits++; | |
f0f1cbaa | 201 | *repp = m_copym(rp->rc_reply, 0, M_COPYALL, |
95f0eacd KM |
202 | M_WAIT); |
203 | rp->rc_timestamp = time.tv_sec; | |
204 | ret = RC_REPLY; | |
205 | } else { | |
206 | nfsstats.srvcache_nonidemdonehits++; | |
207 | rp->rc_state = RC_INPROG; | |
208 | ret = RC_DOIT; | |
209 | } | |
210 | rp->rc_flag &= ~RC_LOCKED; | |
211 | if (rp->rc_flag & RC_WANTED) { | |
212 | rp->rc_flag &= ~RC_WANTED; | |
213 | wakeup((caddr_t)rp); | |
214 | } | |
215 | return (ret); | |
216 | } | |
217 | } | |
218 | nfsstats.srvcache_misses++; | |
219 | rp = nfsrvcachehead.rc_prev; | |
220 | while ((rp->rc_flag & RC_LOCKED) != 0) { | |
221 | rp->rc_flag |= RC_WANTED; | |
170bfd05 | 222 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); |
2c5b44a2 | 223 | rp = nfsrvcachehead.rc_prev; |
95f0eacd | 224 | } |
2c5b44a2 | 225 | rp->rc_flag |= RC_LOCKED; |
95f0eacd | 226 | remque(rp); |
2c5b44a2 KM |
227 | if (rp->rc_prev != &nfsrvcachehead) { |
228 | rp->rc_next->rc_prev = rp->rc_prev; | |
229 | rp->rc_prev->rc_next = rp->rc_next; | |
230 | rp->rc_next = nfsrvcachehead.rc_next; | |
231 | nfsrvcachehead.rc_next = rp; | |
232 | rp->rc_prev = &nfsrvcachehead; | |
233 | rp->rc_next->rc_prev = rp; | |
234 | } | |
95f0eacd | 235 | if (rp->rc_flag & RC_REPMBUF) |
2c5b44a2 KM |
236 | m_freem(rp->rc_reply); |
237 | if (rp->rc_flag & RC_NAM) | |
238 | MFREE(rp->rc_nam, mb); | |
239 | rp->rc_flag &= (RC_LOCKED | RC_WANTED); | |
95f0eacd | 240 | rp->rc_state = RC_INPROG; |
2c5b44a2 KM |
241 | rp->rc_xid = nd->nd_retxid; |
242 | saddr = mtod(nam, struct sockaddr_in *); | |
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_ISO: | |
249 | default: | |
250 | rp->rc_flag |= RC_NAM; | |
251 | rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); | |
252 | break; | |
253 | }; | |
254 | rp->rc_proc = nd->nd_procnum; | |
95f0eacd | 255 | insque(rp, rh); |
2c5b44a2 KM |
256 | rp->rc_flag &= ~RC_LOCKED; |
257 | if (rp->rc_flag & RC_WANTED) { | |
258 | rp->rc_flag &= ~RC_WANTED; | |
259 | wakeup((caddr_t)rp); | |
260 | } | |
95f0eacd KM |
261 | return (RC_DOIT); |
262 | } | |
263 | ||
264 | /* | |
265 | * Update a request cache entry after the rpc has been done | |
266 | */ | |
2c5b44a2 KM |
267 | void |
268 | nfsrv_updatecache(nam, nd, repvalid, repmbuf) | |
95f0eacd | 269 | struct mbuf *nam; |
2c5b44a2 | 270 | register struct nfsd *nd; |
f170c5c2 | 271 | int repvalid; |
95f0eacd KM |
272 | struct mbuf *repmbuf; |
273 | { | |
274 | register struct nfsrvcache *rp; | |
275 | register union rhead *rh; | |
95f0eacd | 276 | |
2c5b44a2 KM |
277 | if (nd->nd_nqlflag != NQL_NOVAL) |
278 | return; | |
279 | rh = &rhead[NFSRCHASH(nd->nd_retxid)]; | |
95f0eacd KM |
280 | loop: |
281 | for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { | |
2c5b44a2 | 282 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && |
4197fd4a | 283 | netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { |
95f0eacd KM |
284 | if ((rp->rc_flag & RC_LOCKED) != 0) { |
285 | rp->rc_flag |= RC_WANTED; | |
170bfd05 | 286 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); |
95f0eacd KM |
287 | goto loop; |
288 | } | |
289 | rp->rc_flag |= RC_LOCKED; | |
290 | rp->rc_state = RC_DONE; | |
f170c5c2 KM |
291 | /* |
292 | * If we have a valid reply update status and save | |
f0f1cbaa KM |
293 | * the reply for non-idempotent rpc's. |
294 | * Otherwise invalidate entry by setting the timestamp | |
295 | * to nil. | |
f170c5c2 KM |
296 | */ |
297 | if (repvalid) { | |
298 | rp->rc_timestamp = time.tv_sec; | |
2c5b44a2 KM |
299 | if (nonidempotent[nd->nd_procnum]) { |
300 | if (repliesstatus[nd->nd_procnum]) { | |
301 | rp->rc_status = nd->nd_repstat; | |
f170c5c2 KM |
302 | rp->rc_flag |= RC_REPSTATUS; |
303 | } else { | |
f0f1cbaa | 304 | rp->rc_reply = m_copym(repmbuf, |
f170c5c2 KM |
305 | 0, M_COPYALL, M_WAIT); |
306 | rp->rc_flag |= RC_REPMBUF; | |
307 | } | |
95f0eacd | 308 | } |
f170c5c2 KM |
309 | } else { |
310 | rp->rc_timestamp = 0; | |
95f0eacd KM |
311 | } |
312 | rp->rc_flag &= ~RC_LOCKED; | |
313 | if (rp->rc_flag & RC_WANTED) { | |
314 | rp->rc_flag &= ~RC_WANTED; | |
315 | wakeup((caddr_t)rp); | |
316 | } | |
317 | return; | |
318 | } | |
319 | } | |
320 | } | |
2c5b44a2 KM |
321 | |
322 | /* | |
323 | * Clean out the cache. Called when the last nfsd terminates. | |
324 | */ | |
325 | void | |
326 | nfsrv_cleancache() | |
327 | { | |
328 | register int i; | |
329 | register struct nfsrvcache *rp = nfsrvcache; | |
330 | register struct nfsrvcache *hp = &nfsrvcachehead; | |
331 | register union rhead *rh = rhead; | |
332 | ||
333 | for (i = NFSRCHSZ; --i >= 0; rh++) { | |
334 | rh->rh_head[0] = rh; | |
335 | rh->rh_head[1] = rh; | |
336 | } | |
337 | hp->rc_next = hp->rc_prev = hp; | |
338 | for (i = NFSRVCACHESIZ; i-- > 0; ) { | |
339 | if (rp->rc_flag & RC_REPMBUF) | |
340 | m_freem(rp->rc_reply); | |
341 | if (rp->rc_flag & RC_NAM) | |
342 | m_freem(rp->rc_nam); | |
343 | rp->rc_state = RC_UNUSED; | |
344 | rp->rc_flag = 0; | |
345 | rp->rc_forw = rp; | |
346 | rp->rc_back = rp; | |
347 | rp->rc_next = hp->rc_next; | |
348 | hp->rc_next->rc_prev = rp; | |
349 | rp->rc_prev = hp; | |
350 | hp->rc_next = rp; | |
351 | rp++; | |
352 | } | |
353 | } |