xref: /original-bsd/sys/nfs/nfs_srvcache.c (revision d24fe13c)
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  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)nfs_srvcache.c	7.13 (Berkeley) 02/15/92
11  */
12 
13 /*
14  * Reference: Chet Juszczak, "Improving the Performance and Correctness
15  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
16  *		pages 53-63. San Diego, February 1989.
17  */
18 #include "param.h"
19 #include "vnode.h"
20 #include "mount.h"
21 #include "kernel.h"
22 #include "systm.h"
23 #include "proc.h"
24 #include "mbuf.h"
25 #include "socket.h"
26 #include "socketvar.h"
27 #include "netinet/in.h"
28 #ifdef ISO
29 #include "netiso/iso.h"
30 #endif
31 #include "nfsm_subs.h"
32 #include "rpcv2.h"
33 #include "nfsv2.h"
34 #include "nfsrvcache.h"
35 #include "nfs.h"
36 #include "nqnfs.h"
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 
55 #define	NETFAMILY(rp) \
56 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
57 
58 /*
59  * Static array that defines which nfs rpc's are nonidempotent
60  */
61 int nonidempotent[NFS_NPROCS] = {
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,
80 	FALSE,
81 	FALSE,
82 	FALSE,
83 	FALSE,
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,
106 	FALSE,
107 	FALSE,
108 	FALSE,
109 	FALSE,
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  */
154 nfsrv_getcache(nam, nd, repp)
155 	struct mbuf *nam;
156 	register struct nfsd *nd;
157 	struct mbuf **repp;
158 {
159 	register struct nfsrvcache *rp;
160 	register union  rhead *rh;
161 	struct mbuf *mb;
162 	struct sockaddr_in *saddr;
163 	caddr_t bpos;
164 	int ret;
165 
166 	if (nd->nd_nqlflag != NQL_NOVAL)
167 		return (RC_DOIT);
168 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
169 loop:
170 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
171 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
172 		nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
173 			if ((rp->rc_flag & RC_LOCKED) != 0) {
174 				rp->rc_flag |= RC_WANTED;
175 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
176 				goto loop;
177 			}
178 			rp->rc_flag |= RC_LOCKED;
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 			}
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++;
195 				nfs_rephead(0, nd, rp->rc_status,
196 				   0, (u_quad_t *)0, repp, &mb, &bpos);
197 				rp->rc_timestamp = time.tv_sec;
198 				ret = RC_REPLY;
199 			} else if (rp->rc_flag & RC_REPMBUF) {
200 				nfsstats.srvcache_idemdonehits++;
201 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
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;
222 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
223 		rp = nfsrvcachehead.rc_prev;
224 	}
225 	rp->rc_flag |= RC_LOCKED;
226 	remque(rp);
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 	}
235 	if (rp->rc_flag & RC_REPMBUF)
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);
240 	rp->rc_state = RC_INPROG;
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;
255 	insque(rp, rh);
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 	}
261 	return (RC_DOIT);
262 }
263 
264 /*
265  * Update a request cache entry after the rpc has been done
266  */
267 void
268 nfsrv_updatecache(nam, nd, repvalid, repmbuf)
269 	struct mbuf *nam;
270 	register struct nfsd *nd;
271 	int repvalid;
272 	struct mbuf *repmbuf;
273 {
274 	register struct nfsrvcache *rp;
275 	register union	rhead *rh;
276 
277 	if (nd->nd_nqlflag != NQL_NOVAL)
278 		return;
279 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
280 loop:
281 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
282 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
283 		nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
284 			if ((rp->rc_flag & RC_LOCKED) != 0) {
285 				rp->rc_flag |= RC_WANTED;
286 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
287 				goto loop;
288 			}
289 			rp->rc_flag |= RC_LOCKED;
290 			rp->rc_state = RC_DONE;
291 			/*
292 			 * If we have a valid reply update status and save
293 			 * the reply for non-idempotent rpc's.
294 			 * Otherwise invalidate entry by setting the timestamp
295 			 * to nil.
296 			 */
297 			if (repvalid) {
298 				rp->rc_timestamp = time.tv_sec;
299 				if (nonidempotent[nd->nd_procnum]) {
300 					if (repliesstatus[nd->nd_procnum]) {
301 						rp->rc_status = nd->nd_repstat;
302 						rp->rc_flag |= RC_REPSTATUS;
303 					} else {
304 						rp->rc_reply = m_copym(repmbuf,
305 							0, M_COPYALL, M_WAIT);
306 						rp->rc_flag |= RC_REPMBUF;
307 					}
308 				}
309 			} else {
310 				rp->rc_timestamp = 0;
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 }
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 }
354