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