386BSD 0.1 development
[unix-history] / usr / src / sys.386bsd / nfs / nfs_srvcache.c
CommitLineData
b688fc87
WJ
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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)nfs_srvcache.c 7.11 (Berkeley) 4/16/91
37 */
38
39/*
40 * Reference: Chet Juszczak, "Improving the Performance and Correctness
41 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
42 * pages 53-63. San Diego, February 1989.
43 */
44
45#include "param.h"
46#include "namei.h"
47#include "vnode.h"
48#include "mount.h"
49#include "kernel.h"
50#include "systm.h"
51#include "mbuf.h"
52#include "socket.h"
53#include "socketvar.h"
54
55#include "../netinet/in.h"
56
57#include "nfsm_subs.h"
58#include "nfsv2.h"
59#include "nfsrvcache.h"
60#include "nfs.h"
61
62#if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
63#define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1))
64#else
65#define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
66#endif
67
68union rhead {
69 union rhead *rh_head[2];
70 struct nfsrvcache *rh_chain[2];
71} rhead[NFSRCHSZ];
72
73static struct nfsrvcache nfsrvcachehead;
74static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
75
76#define TRUE 1
77#define FALSE 0
78
79/*
80 * Static array that defines which nfs rpc's are nonidempotent
81 */
82int nonidempotent[NFS_NPROCS] = {
83 FALSE,
84 FALSE,
85 TRUE,
86 FALSE,
87 FALSE,
88 FALSE,
89 FALSE,
90 FALSE,
91 TRUE,
92 TRUE,
93 TRUE,
94 TRUE,
95 TRUE,
96 TRUE,
97 TRUE,
98 TRUE,
99 FALSE,
100 FALSE,
101};
102
103/* True iff the rpc reply is an nfs status ONLY! */
104static int repliesstatus[NFS_NPROCS] = {
105 FALSE,
106 FALSE,
107 FALSE,
108 FALSE,
109 FALSE,
110 FALSE,
111 FALSE,
112 FALSE,
113 FALSE,
114 FALSE,
115 TRUE,
116 TRUE,
117 TRUE,
118 TRUE,
119 FALSE,
120 TRUE,
121 FALSE,
122 FALSE,
123};
124
125/*
126 * Initialize the server request cache list
127 */
128nfsrv_initcache()
129{
130 register int i;
131 register struct nfsrvcache *rp = nfsrvcache;
132 register struct nfsrvcache *hp = &nfsrvcachehead;
133 register union rhead *rh = rhead;
134
135 for (i = NFSRCHSZ; --i >= 0; rh++) {
136 rh->rh_head[0] = rh;
137 rh->rh_head[1] = rh;
138 }
139 hp->rc_next = hp->rc_prev = hp;
140 for (i = NFSRVCACHESIZ; i-- > 0; ) {
141 rp->rc_state = RC_UNUSED;
142 rp->rc_flag = 0;
143 rp->rc_forw = rp;
144 rp->rc_back = rp;
145 rp->rc_next = hp->rc_next;
146 hp->rc_next->rc_prev = rp;
147 rp->rc_prev = hp;
148 hp->rc_next = rp;
149 rp++;
150 }
151}
152
153/*
154 * Look for the request in the cache
155 * If found then
156 * return action and optionally reply
157 * else
158 * insert it in the cache
159 *
160 * The rules are as follows:
161 * - if in progress, return DROP request
162 * - if completed within DELAY of the current time, return DROP it
163 * - if completed a longer time ago return REPLY if the reply was cached or
164 * return DOIT
165 * Update/add new request at end of lru list
166 */
167nfsrv_getcache(nam, xid, proc, repp)
168 struct mbuf *nam;
169 u_long xid;
170 int proc;
171 struct mbuf **repp;
172{
173 register struct nfsrvcache *rp;
174 register union rhead *rh;
175 struct mbuf *mb;
176 caddr_t bpos;
177 int ret;
178
179 rh = &rhead[NFSRCHASH(xid)];
180loop:
181 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
182 if (xid == rp->rc_xid && proc == rp->rc_proc &&
183 nfs_netaddr_match(nam, &rp->rc_nam)) {
184 if ((rp->rc_flag & RC_LOCKED) != 0) {
185 rp->rc_flag |= RC_WANTED;
186 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
187 goto loop;
188 }
189 rp->rc_flag |= RC_LOCKED;
190 put_at_head(rp);
191 if (rp->rc_state == RC_UNUSED)
192 panic("nfsrv cache");
193 if (rp->rc_state == RC_INPROG ||
194 (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
195 nfsstats.srvcache_inproghits++;
196 ret = RC_DROPIT;
197 } else if (rp->rc_flag & RC_REPSTATUS) {
198 nfsstats.srvcache_idemdonehits++;
199 nfs_rephead(0, xid, rp->rc_status, repp, &mb,
200 &bpos);
201 rp->rc_timestamp = time.tv_sec;
202 ret = RC_REPLY;
203 } else if (rp->rc_flag & RC_REPMBUF) {
204 nfsstats.srvcache_idemdonehits++;
205 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
206 M_WAIT);
207 rp->rc_timestamp = time.tv_sec;
208 ret = RC_REPLY;
209 } else {
210 nfsstats.srvcache_nonidemdonehits++;
211 rp->rc_state = RC_INPROG;
212 ret = RC_DOIT;
213 }
214 rp->rc_flag &= ~RC_LOCKED;
215 if (rp->rc_flag & RC_WANTED) {
216 rp->rc_flag &= ~RC_WANTED;
217 wakeup((caddr_t)rp);
218 }
219 return (ret);
220 }
221 }
222 nfsstats.srvcache_misses++;
223 rp = nfsrvcachehead.rc_prev;
224 while ((rp->rc_flag & RC_LOCKED) != 0) {
225 rp->rc_flag |= RC_WANTED;
226 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
227 }
228 remque(rp);
229 put_at_head(rp);
230 if (rp->rc_flag & RC_REPMBUF)
231 mb = rp->rc_reply;
232 else
233 mb = (struct mbuf *)0;
234 rp->rc_flag = 0;
235 rp->rc_state = RC_INPROG;
236 rp->rc_xid = xid;
237 bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
238 rp->rc_proc = proc;
239 insque(rp, rh);
240 if (mb)
241 m_freem(mb);
242 return (RC_DOIT);
243}
244
245/*
246 * Update a request cache entry after the rpc has been done
247 */
248nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
249 struct mbuf *nam;
250 u_long xid;
251 int proc;
252 int repvalid;
253 int repstat;
254 struct mbuf *repmbuf;
255{
256 register struct nfsrvcache *rp;
257 register union rhead *rh;
258
259 rh = &rhead[NFSRCHASH(xid)];
260loop:
261 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
262 if (xid == rp->rc_xid && proc == rp->rc_proc &&
263 nfs_netaddr_match(nam, &rp->rc_nam)) {
264 if ((rp->rc_flag & RC_LOCKED) != 0) {
265 rp->rc_flag |= RC_WANTED;
266 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
267 goto loop;
268 }
269 rp->rc_flag |= RC_LOCKED;
270 rp->rc_state = RC_DONE;
271 /*
272 * If we have a valid reply update status and save
273 * the reply for non-idempotent rpc's.
274 * Otherwise invalidate entry by setting the timestamp
275 * to nil.
276 */
277 if (repvalid) {
278 rp->rc_timestamp = time.tv_sec;
279 if (nonidempotent[proc]) {
280 if (repliesstatus[proc]) {
281 rp->rc_status = repstat;
282 rp->rc_flag |= RC_REPSTATUS;
283 } else {
284 rp->rc_reply = m_copym(repmbuf,
285 0, M_COPYALL, M_WAIT);
286 rp->rc_flag |= RC_REPMBUF;
287 }
288 }
289 } else {
290 rp->rc_timestamp = 0;
291 }
292 rp->rc_flag &= ~RC_LOCKED;
293 if (rp->rc_flag & RC_WANTED) {
294 rp->rc_flag &= ~RC_WANTED;
295 wakeup((caddr_t)rp);
296 }
297 return;
298 }
299 }
300}