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