date and time created 92/11/15 16:50:57 by ralph
[unix-history] / usr / src / sys / nfs / nfs_srvcache.c
CommitLineData
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 40long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
95f0eacd 41
96964be4
KM
42#define NFSRCHASH(xid) (((xid) + ((xid) >> 16)) & rheadhash)
43static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
44static struct nfsrvcache **rheadhtbl;
45static 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 56int 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! */
83static 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 */
112nfsrv_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 132nfsrv_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 146loop:
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
263void
264nfsrv_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 274loop:
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 */
319void
320nfsrv_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}