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