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 | * |
5548a02f | 10 | * @(#)nfs_srvcache.c 7.19 (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> | |
96964be4 | 25 | #include <sys/malloc.h> |
d615f2fe KM |
26 | #include <sys/socket.h> |
27 | #include <sys/socketvar.h> | |
5548a02f | 28 | |
d615f2fe | 29 | #include <netinet/in.h> |
2c5b44a2 | 30 | #ifdef ISO |
d615f2fe | 31 | #include <netiso/iso.h> |
2c5b44a2 | 32 | #endif |
d615f2fe KM |
33 | #include <nfs/nfsm_subs.h> |
34 | #include <nfs/rpcv2.h> | |
35 | #include <nfs/nfsv2.h> | |
36 | #include <nfs/nfs.h> | |
37 | #include <nfs/nfsrvcache.h> | |
38 | #include <nfs/nqnfs.h> | |
95f0eacd | 39 | |
96964be4 | 40 | long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; |
95f0eacd | 41 | |
96964be4 KM |
42 | #define NFSRCHASH(xid) (((xid) + ((xid) >> 16)) & rheadhash) |
43 | static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead; | |
44 | static struct nfsrvcache **rheadhtbl; | |
45 | static u_long rheadhash; | |
95f0eacd KM |
46 | |
47 | #define TRUE 1 | |
48 | #define FALSE 0 | |
49 | ||
2c5b44a2 KM |
50 | #define NETFAMILY(rp) \ |
51 | (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) | |
52 | ||
95f0eacd KM |
53 | /* |
54 | * Static array that defines which nfs rpc's are nonidempotent | |
55 | */ | |
cdf8c1c6 | 56 | int nonidempotent[NFS_NPROCS] = { |
95f0eacd KM |
57 | FALSE, |
58 | FALSE, | |
59 | TRUE, | |
60 | FALSE, | |
61 | FALSE, | |
62 | FALSE, | |
63 | FALSE, | |
64 | FALSE, | |
65 | TRUE, | |
66 | TRUE, | |
67 | TRUE, | |
68 | TRUE, | |
69 | TRUE, | |
70 | TRUE, | |
71 | TRUE, | |
72 | TRUE, | |
73 | FALSE, | |
74 | FALSE, | |
2c5b44a2 KM |
75 | FALSE, |
76 | FALSE, | |
77 | FALSE, | |
78 | FALSE, | |
b330a7ec | 79 | FALSE, |
95f0eacd KM |
80 | }; |
81 | ||
82 | /* True iff the rpc reply is an nfs status ONLY! */ | |
83 | static int repliesstatus[NFS_NPROCS] = { | |
84 | FALSE, | |
85 | FALSE, | |
86 | FALSE, | |
87 | FALSE, | |
88 | FALSE, | |
89 | FALSE, | |
90 | FALSE, | |
91 | FALSE, | |
92 | FALSE, | |
93 | FALSE, | |
94 | TRUE, | |
95 | TRUE, | |
96 | TRUE, | |
97 | TRUE, | |
98 | FALSE, | |
99 | TRUE, | |
100 | FALSE, | |
101 | FALSE, | |
2c5b44a2 KM |
102 | FALSE, |
103 | FALSE, | |
104 | FALSE, | |
105 | FALSE, | |
b330a7ec | 106 | TRUE, |
95f0eacd KM |
107 | }; |
108 | ||
109 | /* | |
110 | * Initialize the server request cache list | |
111 | */ | |
112 | nfsrv_initcache() | |
113 | { | |
95f0eacd | 114 | |
96964be4 | 115 | rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash); |
95f0eacd KM |
116 | } |
117 | ||
118 | /* | |
119 | * Look for the request in the cache | |
120 | * If found then | |
121 | * return action and optionally reply | |
122 | * else | |
123 | * insert it in the cache | |
124 | * | |
125 | * The rules are as follows: | |
126 | * - if in progress, return DROP request | |
127 | * - if completed within DELAY of the current time, return DROP it | |
128 | * - if completed a longer time ago return REPLY if the reply was cached or | |
129 | * return DOIT | |
130 | * Update/add new request at end of lru list | |
131 | */ | |
2c5b44a2 | 132 | nfsrv_getcache(nam, nd, repp) |
95f0eacd | 133 | struct mbuf *nam; |
2c5b44a2 | 134 | register struct nfsd *nd; |
95f0eacd KM |
135 | struct mbuf **repp; |
136 | { | |
96964be4 | 137 | register struct nfsrvcache *rp, *rq, **rpp; |
95f0eacd | 138 | struct mbuf *mb; |
2c5b44a2 | 139 | struct sockaddr_in *saddr; |
95f0eacd KM |
140 | caddr_t bpos; |
141 | int ret; | |
142 | ||
2c5b44a2 KM |
143 | if (nd->nd_nqlflag != NQL_NOVAL) |
144 | return (RC_DOIT); | |
96964be4 | 145 | rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)]; |
95f0eacd | 146 | loop: |
96964be4 | 147 | for (rp = *rpp; rp; rp = rp->rc_forw) { |
2c5b44a2 | 148 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && |
b330a7ec | 149 | netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { |
95f0eacd KM |
150 | if ((rp->rc_flag & RC_LOCKED) != 0) { |
151 | rp->rc_flag |= RC_WANTED; | |
170bfd05 | 152 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); |
95f0eacd KM |
153 | goto loop; |
154 | } | |
155 | rp->rc_flag |= RC_LOCKED; | |
96964be4 KM |
156 | /* If not at end of LRU chain, move it there */ |
157 | if (rp->rc_next) { | |
158 | /* remove from LRU chain */ | |
159 | *rp->rc_prev = rp->rc_next; | |
2c5b44a2 | 160 | rp->rc_next->rc_prev = rp->rc_prev; |
96964be4 KM |
161 | /* and replace at end of it */ |
162 | rp->rc_next = NULL; | |
163 | rp->rc_prev = nfsrvlrutail; | |
164 | *nfsrvlrutail = rp; | |
165 | nfsrvlrutail = &rp->rc_next; | |
2c5b44a2 | 166 | } |
95f0eacd KM |
167 | if (rp->rc_state == RC_UNUSED) |
168 | panic("nfsrv cache"); | |
169 | if (rp->rc_state == RC_INPROG || | |
170 | (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { | |
171 | nfsstats.srvcache_inproghits++; | |
172 | ret = RC_DROPIT; | |
173 | } else if (rp->rc_flag & RC_REPSTATUS) { | |
174 | nfsstats.srvcache_idemdonehits++; | |
2c5b44a2 KM |
175 | nfs_rephead(0, nd, rp->rc_status, |
176 | 0, (u_quad_t *)0, repp, &mb, &bpos); | |
95f0eacd KM |
177 | rp->rc_timestamp = time.tv_sec; |
178 | ret = RC_REPLY; | |
179 | } else if (rp->rc_flag & RC_REPMBUF) { | |
180 | nfsstats.srvcache_idemdonehits++; | |
f0f1cbaa | 181 | *repp = m_copym(rp->rc_reply, 0, M_COPYALL, |
95f0eacd KM |
182 | M_WAIT); |
183 | rp->rc_timestamp = time.tv_sec; | |
184 | ret = RC_REPLY; | |
185 | } else { | |
186 | nfsstats.srvcache_nonidemdonehits++; | |
187 | rp->rc_state = RC_INPROG; | |
188 | ret = RC_DOIT; | |
189 | } | |
190 | rp->rc_flag &= ~RC_LOCKED; | |
191 | if (rp->rc_flag & RC_WANTED) { | |
192 | rp->rc_flag &= ~RC_WANTED; | |
193 | wakeup((caddr_t)rp); | |
194 | } | |
195 | return (ret); | |
196 | } | |
197 | } | |
198 | nfsstats.srvcache_misses++; | |
96964be4 KM |
199 | if (numnfsrvcache < desirednfsrvcache) { |
200 | rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, | |
201 | M_NFSD, M_WAITOK); | |
202 | bzero((char *)rp, sizeof *rp); | |
203 | numnfsrvcache++; | |
204 | rp->rc_flag = RC_LOCKED; | |
205 | } else { | |
206 | rp = nfsrvlruhead; | |
207 | while ((rp->rc_flag & RC_LOCKED) != 0) { | |
208 | rp->rc_flag |= RC_WANTED; | |
209 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); | |
210 | rp = nfsrvlruhead; | |
211 | } | |
212 | rp->rc_flag |= RC_LOCKED; | |
213 | /* remove from hash chain */ | |
214 | if (rq = rp->rc_forw) | |
215 | rq->rc_back = rp->rc_back; | |
216 | *rp->rc_back = rq; | |
217 | /* remove from LRU chain */ | |
218 | *rp->rc_prev = rp->rc_next; | |
2c5b44a2 | 219 | rp->rc_next->rc_prev = rp->rc_prev; |
96964be4 KM |
220 | if (rp->rc_flag & RC_REPMBUF) |
221 | m_freem(rp->rc_reply); | |
222 | if (rp->rc_flag & RC_NAM) | |
223 | MFREE(rp->rc_nam, mb); | |
224 | rp->rc_flag &= (RC_LOCKED | RC_WANTED); | |
2c5b44a2 | 225 | } |
96964be4 KM |
226 | /* place at end of LRU list */ |
227 | rp->rc_next = NULL; | |
228 | rp->rc_prev = nfsrvlrutail; | |
229 | *nfsrvlrutail = rp; | |
230 | nfsrvlrutail = &rp->rc_next; | |
95f0eacd | 231 | rp->rc_state = RC_INPROG; |
2c5b44a2 KM |
232 | rp->rc_xid = nd->nd_retxid; |
233 | saddr = mtod(nam, struct sockaddr_in *); | |
234 | switch (saddr->sin_family) { | |
235 | case AF_INET: | |
236 | rp->rc_flag |= RC_INETADDR; | |
237 | rp->rc_inetaddr = saddr->sin_addr.s_addr; | |
238 | break; | |
239 | case AF_ISO: | |
240 | default: | |
241 | rp->rc_flag |= RC_NAM; | |
242 | rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); | |
243 | break; | |
244 | }; | |
245 | rp->rc_proc = nd->nd_procnum; | |
96964be4 KM |
246 | /* insert into hash chain */ |
247 | if (rq = *rpp) | |
248 | rq->rc_back = &rp->rc_forw; | |
e5184d3e | 249 | rp->rc_forw = rq; |
96964be4 KM |
250 | rp->rc_back = rpp; |
251 | *rpp = rp; | |
2c5b44a2 KM |
252 | rp->rc_flag &= ~RC_LOCKED; |
253 | if (rp->rc_flag & RC_WANTED) { | |
254 | rp->rc_flag &= ~RC_WANTED; | |
255 | wakeup((caddr_t)rp); | |
256 | } | |
95f0eacd KM |
257 | return (RC_DOIT); |
258 | } | |
259 | ||
260 | /* | |
261 | * Update a request cache entry after the rpc has been done | |
262 | */ | |
2c5b44a2 KM |
263 | void |
264 | nfsrv_updatecache(nam, nd, repvalid, repmbuf) | |
95f0eacd | 265 | struct mbuf *nam; |
2c5b44a2 | 266 | register struct nfsd *nd; |
f170c5c2 | 267 | int repvalid; |
95f0eacd KM |
268 | struct mbuf *repmbuf; |
269 | { | |
270 | register struct nfsrvcache *rp; | |
95f0eacd | 271 | |
2c5b44a2 KM |
272 | if (nd->nd_nqlflag != NQL_NOVAL) |
273 | return; | |
95f0eacd | 274 | loop: |
96964be4 | 275 | for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) { |
2c5b44a2 | 276 | if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && |
b330a7ec | 277 | netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { |
95f0eacd KM |
278 | if ((rp->rc_flag & RC_LOCKED) != 0) { |
279 | rp->rc_flag |= RC_WANTED; | |
170bfd05 | 280 | (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); |
95f0eacd KM |
281 | goto loop; |
282 | } | |
283 | rp->rc_flag |= RC_LOCKED; | |
284 | rp->rc_state = RC_DONE; | |
f170c5c2 KM |
285 | /* |
286 | * If we have a valid reply update status and save | |
f0f1cbaa KM |
287 | * the reply for non-idempotent rpc's. |
288 | * Otherwise invalidate entry by setting the timestamp | |
289 | * to nil. | |
f170c5c2 KM |
290 | */ |
291 | if (repvalid) { | |
292 | rp->rc_timestamp = time.tv_sec; | |
2c5b44a2 KM |
293 | if (nonidempotent[nd->nd_procnum]) { |
294 | if (repliesstatus[nd->nd_procnum]) { | |
295 | rp->rc_status = nd->nd_repstat; | |
f170c5c2 KM |
296 | rp->rc_flag |= RC_REPSTATUS; |
297 | } else { | |
f0f1cbaa | 298 | rp->rc_reply = m_copym(repmbuf, |
f170c5c2 KM |
299 | 0, M_COPYALL, M_WAIT); |
300 | rp->rc_flag |= RC_REPMBUF; | |
301 | } | |
95f0eacd | 302 | } |
f170c5c2 KM |
303 | } else { |
304 | rp->rc_timestamp = 0; | |
95f0eacd KM |
305 | } |
306 | rp->rc_flag &= ~RC_LOCKED; | |
307 | if (rp->rc_flag & RC_WANTED) { | |
308 | rp->rc_flag &= ~RC_WANTED; | |
309 | wakeup((caddr_t)rp); | |
310 | } | |
311 | return; | |
312 | } | |
313 | } | |
314 | } | |
2c5b44a2 KM |
315 | |
316 | /* | |
317 | * Clean out the cache. Called when the last nfsd terminates. | |
318 | */ | |
319 | void | |
320 | nfsrv_cleancache() | |
321 | { | |
96964be4 | 322 | register struct nfsrvcache *rp, *nextrp; |
2c5b44a2 | 323 | |
96964be4 KM |
324 | for (rp = nfsrvlruhead; rp; rp = nextrp) { |
325 | nextrp = rp->rc_next; | |
326 | free(rp, M_NFSD); | |
2c5b44a2 | 327 | } |
96964be4 KM |
328 | bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *)); |
329 | numnfsrvcache = 0; | |
2c5b44a2 | 330 | } |