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