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