1 /*-
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. 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 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95
33 */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 /*
39 * Reference: Chet Juszczak, "Improving the Performance and Correctness
40 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
41 * pages 53-63. San Diego, February 1989.
42 */
43 #include <sys/param.h>
44 #include <sys/malloc.h>
45 #include <sys/mount.h>
46 #include <sys/systm.h>
47 #include <sys/lock.h>
48 #include <sys/mbuf.h>
49 #include <sys/mutex.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h> /* for sodupsockaddr */
52
53 #include <netinet/in.h>
54 #include <nfs/rpcv2.h>
55 #include <nfs/nfsproto.h>
56 #include <nfsserver/nfs.h>
57 #include <nfsserver/nfsrvcache.h>
58
59 static long numnfsrvcache;
60 static long desirednfsrvcache = NFSRVCACHESIZ;
61
62 #define NFSRCHASH(xid) \
63 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
64 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
65 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
66 static u_long nfsrvhash;
67
68 #define TRUE 1
69 #define FALSE 0
70
71 #define NETFAMILY(rp) \
72 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
73
74 /*
75 * Static array that defines which nfs rpc's are nonidempotent
76 */
77 static const int nonidempotent[NFS_NPROCS] = {
78 FALSE,
79 FALSE,
80 TRUE,
81 FALSE,
82 FALSE,
83 FALSE,
84 FALSE,
85 TRUE,
86 TRUE,
87 TRUE,
88 TRUE,
89 TRUE,
90 TRUE,
91 TRUE,
92 TRUE,
93 TRUE,
94 FALSE,
95 FALSE,
96 FALSE,
97 FALSE,
98 FALSE,
99 FALSE,
100 FALSE,
101 };
102
103 /* True iff the rpc reply is an nfs status ONLY! */
104 static const int nfsv2_repstat[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 */
128 void
129 nfsrv_initcache(void)
130 {
131
132 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
133 TAILQ_INIT(&nfsrvlruhead);
134 }
135
136 /*
137 * Look for the request in the cache
138 * If found then
139 * return action and optionally reply
140 * else
141 * insert it in the cache
142 *
143 * The rules are as follows:
144 * - if in progress, return DROP request
145 * - if completed within DELAY of the current time, return DROP it
146 * - if completed a longer time ago return REPLY if the reply was cached or
147 * return DOIT
148 * Update/add new request at end of lru list
149 */
150 int
151 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
152 {
153 struct nfsrvcache *rp;
154 struct mbuf *mb;
155 struct sockaddr_in *saddr;
156 caddr_t bpos;
157 int ret;
158
159 NFSD_LOCK_ASSERT();
160
161 /*
162 * Don't cache recent requests for reliable transport protocols.
163 * (Maybe we should for the case of a reconnect, but..)
164 */
165 if (!nd->nd_nam2)
166 return (RC_DOIT);
167 loop:
168 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
169 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
170 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
171 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
172 if ((rp->rc_flag & RC_LOCKED) != 0) {
173 rp->rc_flag |= RC_WANTED;
174 (void) msleep(rp, &nfsd_mtx, PZERO-1,
175 "nfsrc", 0);
176 goto loop;
177 }
178 rp->rc_flag |= RC_LOCKED;
179 /* If not at end of LRU chain, move it there */
180 if (TAILQ_NEXT(rp, rc_lru)) {
181 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
182 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
183 }
184 if (rp->rc_state == RC_UNUSED)
185 panic("nfsrv cache");
186 if (rp->rc_state == RC_INPROG) {
187 nfsrvstats.srvcache_inproghits++;
188 ret = RC_DROPIT;
189 } else if (rp->rc_flag & RC_REPSTATUS) {
190 nfsrvstats.srvcache_nonidemdonehits++;
191 *repp = nfs_rephead(0, nd, rp->rc_status,
192 &mb, &bpos);
193 ret = RC_REPLY;
194 } else if (rp->rc_flag & RC_REPMBUF) {
195 nfsrvstats.srvcache_nonidemdonehits++;
196 NFSD_UNLOCK();
197 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
198 M_TRYWAIT);
199 NFSD_LOCK();
200 ret = RC_REPLY;
201 } else {
202 nfsrvstats.srvcache_idemdonehits++;
203 rp->rc_state = RC_INPROG;
204 ret = RC_DOIT;
205 }
206 rp->rc_flag &= ~RC_LOCKED;
207 if (rp->rc_flag & RC_WANTED) {
208 rp->rc_flag &= ~RC_WANTED;
209 wakeup(rp);
210 }
211 return (ret);
212 }
213 }
214 nfsrvstats.srvcache_misses++;
215 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
216 if (numnfsrvcache < desirednfsrvcache) {
217 NFSD_UNLOCK();
218 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
219 M_NFSD, M_WAITOK | M_ZERO);
220 NFSD_LOCK();
221 numnfsrvcache++;
222 rp->rc_flag = RC_LOCKED;
223 } else {
224 rp = TAILQ_FIRST(&nfsrvlruhead);
225 while ((rp->rc_flag & RC_LOCKED) != 0) {
226 rp->rc_flag |= RC_WANTED;
227 (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
228 rp = TAILQ_FIRST(&nfsrvlruhead);
229 }
230 rp->rc_flag |= RC_LOCKED;
231 LIST_REMOVE(rp, rc_hash);
232 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
233 if (rp->rc_flag & RC_REPMBUF)
234 m_freem(rp->rc_reply);
235 if (rp->rc_flag & RC_NAM)
236 FREE(rp->rc_nam, M_SONAME);
237 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
238 }
239 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
240 rp->rc_state = RC_INPROG;
241 rp->rc_xid = nd->nd_retxid;
242 saddr = (struct sockaddr_in *)nd->nd_nam;
243 switch (saddr->sin_family) {
244 case AF_INET:
245 rp->rc_flag |= RC_INETADDR;
246 rp->rc_inetaddr = saddr->sin_addr.s_addr;
247 break;
248 /* case AF_INET6: */
249 /* case AF_ISO: */
250 default:
251 /*
252 * XXXRW: Seems like we should only set RC_NAM if we
253 * actually manage to set rc_nam to something non-NULL.
254 */
255 rp->rc_flag |= RC_NAM;
256 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
257 break;
258 };
259 rp->rc_proc = nd->nd_procnum;
260 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
261 rp->rc_flag &= ~RC_LOCKED;
262 if (rp->rc_flag & RC_WANTED) {
263 rp->rc_flag &= ~RC_WANTED;
264 wakeup(rp);
265 }
266 return (RC_DOIT);
267 }
268
269 /*
270 * Update a request cache entry after the rpc has been done
271 */
272 void
273 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
274 {
275 struct nfsrvcache *rp;
276
277 NFSD_LOCK_ASSERT();
278
279 if (!nd->nd_nam2)
280 return;
281 loop:
282 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
283 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
284 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
285 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
286 if ((rp->rc_flag & RC_LOCKED) != 0) {
287 rp->rc_flag |= RC_WANTED;
288 (void) msleep(rp, &nfsd_mtx, PZERO-1,
289 "nfsrc", 0);
290 goto loop;
291 }
292 rp->rc_flag |= RC_LOCKED;
293 if (rp->rc_state == RC_DONE) {
294 /*
295 * This can occur if the cache is too small.
296 * Retransmits of the same request aren't
297 * dropped so we may see the operation
298 * complete more then once.
299 */
300 if (rp->rc_flag & RC_REPMBUF) {
301 m_freem(rp->rc_reply);
302 rp->rc_flag &= ~RC_REPMBUF;
303 }
304 }
305 rp->rc_state = RC_DONE;
306 /*
307 * If we have a valid reply update status and save
308 * the reply for non-idempotent rpc's.
309 */
310 if (repvalid && nonidempotent[nd->nd_procnum]) {
311 if ((nd->nd_flag & ND_NFSV3) == 0 &&
312 nfsv2_repstat[
313 nfsrvv2_procid[nd->nd_procnum]]) {
314 rp->rc_status = nd->nd_repstat;
315 rp->rc_flag |= RC_REPSTATUS;
316 } else {
317 NFSD_UNLOCK();
318 rp->rc_reply = m_copym(repmbuf,
319 0, M_COPYALL, M_TRYWAIT);
320 NFSD_LOCK();
321 rp->rc_flag |= RC_REPMBUF;
322 }
323 }
324 rp->rc_flag &= ~RC_LOCKED;
325 if (rp->rc_flag & RC_WANTED) {
326 rp->rc_flag &= ~RC_WANTED;
327 wakeup(rp);
328 }
329 return;
330 }
331 }
332 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
333 }
334
335 /*
336 * Clean out the cache. Called when the last nfsd terminates.
337 */
338 void
339 nfsrv_cleancache(void)
340 {
341 struct nfsrvcache *rp, *nextrp;
342
343 NFSD_LOCK_ASSERT();
344
345 for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
346 nextrp = TAILQ_NEXT(rp, rc_lru);
347 LIST_REMOVE(rp, rc_hash);
348 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
349 if (rp->rc_flag & RC_REPMBUF)
350 m_freem(rp->rc_reply);
351 if (rp->rc_flag & RC_NAM)
352 free(rp->rc_nam, M_SONAME);
353 free(rp, M_NFSD);
354 }
355 numnfsrvcache = 0;
356 }
Cache object: 16c95b446ca0144b69173ba9cab36ba4
|