xref: /original-bsd/sys/nfs/nfs_srvcache.c (revision 9b5efc43)
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.9 (Berkeley) 06/28/90
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 
19 #include "param.h"
20 #include "user.h"
21 #include "vnode.h"
22 #include "mount.h"
23 #include "kernel.h"
24 #include "systm.h"
25 #include "mbuf.h"
26 #include "socket.h"
27 #include "socketvar.h"
28 #include "../netinet/in.h"
29 #include "nfsm_subs.h"
30 #include "nfsv2.h"
31 #include "nfsrvcache.h"
32 #include "nfs.h"
33 
34 #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
35 #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
36 #else
37 #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
38 #endif
39 
40 union rhead {
41 	union  rhead *rh_head[2];
42 	struct nfsrvcache *rh_chain[2];
43 } rhead[NFSRCHSZ];
44 
45 static struct nfsrvcache nfsrvcachehead;
46 static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
47 
48 #define TRUE	1
49 #define	FALSE	0
50 
51 /*
52  * Static array that defines which nfs rpc's are nonidempotent
53  */
54 int nonidempotent[NFS_NPROCS] = {
55 	FALSE,
56 	FALSE,
57 	TRUE,
58 	FALSE,
59 	FALSE,
60 	FALSE,
61 	FALSE,
62 	FALSE,
63 	TRUE,
64 	TRUE,
65 	TRUE,
66 	TRUE,
67 	TRUE,
68 	TRUE,
69 	TRUE,
70 	TRUE,
71 	FALSE,
72 	FALSE,
73 };
74 
75 /* True iff the rpc reply is an nfs status ONLY! */
76 static int repliesstatus[NFS_NPROCS] = {
77 	FALSE,
78 	FALSE,
79 	FALSE,
80 	FALSE,
81 	FALSE,
82 	FALSE,
83 	FALSE,
84 	FALSE,
85 	FALSE,
86 	FALSE,
87 	TRUE,
88 	TRUE,
89 	TRUE,
90 	TRUE,
91 	FALSE,
92 	TRUE,
93 	FALSE,
94 	FALSE,
95 };
96 
97 /*
98  * Initialize the server request cache list
99  */
100 nfsrv_initcache()
101 {
102 	register int i;
103 	register struct nfsrvcache *rp = nfsrvcache;
104 	register struct nfsrvcache *hp = &nfsrvcachehead;
105 	register union  rhead *rh = rhead;
106 
107 	for (i = NFSRCHSZ; --i >= 0; rh++) {
108 		rh->rh_head[0] = rh;
109 		rh->rh_head[1] = rh;
110 	}
111 	hp->rc_next = hp->rc_prev = hp;
112 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
113 		rp->rc_state = RC_UNUSED;
114 		rp->rc_flag = 0;
115 		rp->rc_forw = rp;
116 		rp->rc_back = rp;
117 		rp->rc_next = hp->rc_next;
118 		hp->rc_next->rc_prev = rp;
119 		rp->rc_prev = hp;
120 		hp->rc_next = rp;
121 		rp++;
122 	}
123 }
124 
125 /*
126  * Look for the request in the cache
127  * If found then
128  *    return action and optionally reply
129  * else
130  *    insert it in the cache
131  *
132  * The rules are as follows:
133  * - if in progress, return DROP request
134  * - if completed within DELAY of the current time, return DROP it
135  * - if completed a longer time ago return REPLY if the reply was cached or
136  *   return DOIT
137  * Update/add new request at end of lru list
138  */
139 nfsrv_getcache(nam, xid, proc, repp)
140 	struct mbuf *nam;
141 	u_long xid;
142 	int proc;
143 	struct mbuf **repp;
144 {
145 	register struct nfsrvcache *rp;
146 	register union  rhead *rh;
147 	struct mbuf *mb;
148 	caddr_t bpos;
149 	int ret;
150 
151 	rh = &rhead[NFSRCHASH(xid)];
152 loop:
153 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
154 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
155 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
156 			if ((rp->rc_flag & RC_LOCKED) != 0) {
157 				rp->rc_flag |= RC_WANTED;
158 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
159 				goto loop;
160 			}
161 			rp->rc_flag |= RC_LOCKED;
162 			put_at_head(rp);
163 			if (rp->rc_state == RC_UNUSED)
164 				panic("nfsrv cache");
165 			if (rp->rc_state == RC_INPROG ||
166 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
167 				nfsstats.srvcache_inproghits++;
168 				ret = RC_DROPIT;
169 			} else if (rp->rc_flag & RC_REPSTATUS) {
170 				nfsstats.srvcache_idemdonehits++;
171 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
172 					&bpos);
173 				rp->rc_timestamp = time.tv_sec;
174 				ret = RC_REPLY;
175 			} else if (rp->rc_flag & RC_REPMBUF) {
176 				nfsstats.srvcache_idemdonehits++;
177 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
178 						M_WAIT);
179 				rp->rc_timestamp = time.tv_sec;
180 				ret = RC_REPLY;
181 			} else {
182 				nfsstats.srvcache_nonidemdonehits++;
183 				rp->rc_state = RC_INPROG;
184 				ret = RC_DOIT;
185 			}
186 			rp->rc_flag &= ~RC_LOCKED;
187 			if (rp->rc_flag & RC_WANTED) {
188 				rp->rc_flag &= ~RC_WANTED;
189 				wakeup((caddr_t)rp);
190 			}
191 			return (ret);
192 		}
193 	}
194 	nfsstats.srvcache_misses++;
195 	rp = nfsrvcachehead.rc_prev;
196 	while ((rp->rc_flag & RC_LOCKED) != 0) {
197 		rp->rc_flag |= RC_WANTED;
198 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
199 	}
200 	remque(rp);
201 	put_at_head(rp);
202 	if (rp->rc_flag & RC_REPMBUF)
203 		mb = rp->rc_reply;
204 	else
205 		mb = (struct mbuf *)0;
206 	rp->rc_flag = 0;
207 	rp->rc_state = RC_INPROG;
208 	rp->rc_xid = xid;
209 	bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
210 	rp->rc_proc = proc;
211 	insque(rp, rh);
212 	if (mb)
213 		m_freem(mb);
214 	return (RC_DOIT);
215 }
216 
217 /*
218  * Update a request cache entry after the rpc has been done
219  */
220 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
221 	struct mbuf *nam;
222 	u_long xid;
223 	int proc;
224 	int repvalid;
225 	int repstat;
226 	struct mbuf *repmbuf;
227 {
228 	register struct nfsrvcache *rp;
229 	register union	rhead *rh;
230 
231 	rh = &rhead[NFSRCHASH(xid)];
232 loop:
233 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
234 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
235 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
236 			if ((rp->rc_flag & RC_LOCKED) != 0) {
237 				rp->rc_flag |= RC_WANTED;
238 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
239 				goto loop;
240 			}
241 			rp->rc_flag |= RC_LOCKED;
242 			rp->rc_state = RC_DONE;
243 			/*
244 			 * If we have a valid reply update status and save
245 			 * the reply for non-idempotent rpc's.
246 			 * Otherwise invalidate entry by setting the timestamp
247 			 * to nil.
248 			 */
249 			if (repvalid) {
250 				rp->rc_timestamp = time.tv_sec;
251 				if (nonidempotent[proc]) {
252 					if (repliesstatus[proc]) {
253 						rp->rc_status = repstat;
254 						rp->rc_flag |= RC_REPSTATUS;
255 					} else {
256 						rp->rc_reply = m_copym(repmbuf,
257 							0, M_COPYALL, M_WAIT);
258 						rp->rc_flag |= RC_REPMBUF;
259 					}
260 				}
261 			} else {
262 				rp->rc_timestamp = 0;
263 			}
264 			rp->rc_flag &= ~RC_LOCKED;
265 			if (rp->rc_flag & RC_WANTED) {
266 				rp->rc_flag &= ~RC_WANTED;
267 				wakeup((caddr_t)rp);
268 			}
269 			return;
270 		}
271 	}
272 }
273