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: releng/6.2/sys/nfsserver/nfs_srvcache.c 161221 2006-08-11 19:13:25Z jhb $");
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 * Teardown the server request cache list
138 */
139 void
140 nfsrv_destroycache(void)
141 {
142
143 KASSERT(TAILQ_EMPTY(&nfsrvlruhead), ("%s: pending requests", __func__));
144 hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
145 }
146
147 /*
148 * Look for the request in the cache
149 * If found then
150 * return action and optionally reply
151 * else
152 * insert it in the cache
153 *
154 * The rules are as follows:
155 * - if in progress, return DROP request
156 * - if completed within DELAY of the current time, return DROP it
157 * - if completed a longer time ago return REPLY if the reply was cached or
158 * return DOIT
159 * Update/add new request at end of lru list
160 */
161 int
162 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
163 {
164 struct nfsrvcache *rp;
165 struct mbuf *mb;
166 struct sockaddr_in *saddr;
167 caddr_t bpos;
168 int ret;
169
170 NFSD_LOCK_ASSERT();
171
172 /*
173 * Don't cache recent requests for reliable transport protocols.
174 * (Maybe we should for the case of a reconnect, but..)
175 */
176 if (!nd->nd_nam2)
177 return (RC_DOIT);
178 loop:
179 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
180 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
181 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
182 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
183 if ((rp->rc_flag & RC_LOCKED) != 0) {
184 rp->rc_flag |= RC_WANTED;
185 (void) msleep(rp, &nfsd_mtx, PZERO-1,
186 "nfsrc", 0);
187 goto loop;
188 }
189 rp->rc_flag |= RC_LOCKED;
190 /* If not at end of LRU chain, move it there */
191 if (TAILQ_NEXT(rp, rc_lru)) {
192 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
193 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
194 }
195 if (rp->rc_state == RC_UNUSED)
196 panic("nfsrv cache");
197 if (rp->rc_state == RC_INPROG) {
198 nfsrvstats.srvcache_inproghits++;
199 ret = RC_DROPIT;
200 } else if (rp->rc_flag & RC_REPSTATUS) {
201 nfsrvstats.srvcache_nonidemdonehits++;
202 *repp = nfs_rephead(0, nd, rp->rc_status,
203 &mb, &bpos);
204 ret = RC_REPLY;
205 } else if (rp->rc_flag & RC_REPMBUF) {
206 nfsrvstats.srvcache_nonidemdonehits++;
207 NFSD_UNLOCK();
208 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
209 M_TRYWAIT);
210 NFSD_LOCK();
211 ret = RC_REPLY;
212 } else {
213 nfsrvstats.srvcache_idemdonehits++;
214 rp->rc_state = RC_INPROG;
215 ret = RC_DOIT;
216 }
217 rp->rc_flag &= ~RC_LOCKED;
218 if (rp->rc_flag & RC_WANTED) {
219 rp->rc_flag &= ~RC_WANTED;
220 wakeup(rp);
221 }
222 return (ret);
223 }
224 }
225 nfsrvstats.srvcache_misses++;
226 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
227 if (numnfsrvcache < desirednfsrvcache) {
228 NFSD_UNLOCK();
229 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
230 M_NFSD, M_WAITOK | M_ZERO);
231 NFSD_LOCK();
232 numnfsrvcache++;
233 rp->rc_flag = RC_LOCKED;
234 } else {
235 rp = TAILQ_FIRST(&nfsrvlruhead);
236 while ((rp->rc_flag & RC_LOCKED) != 0) {
237 rp->rc_flag |= RC_WANTED;
238 (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
239 rp = TAILQ_FIRST(&nfsrvlruhead);
240 }
241 rp->rc_flag |= RC_LOCKED;
242 LIST_REMOVE(rp, rc_hash);
243 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
244 if (rp->rc_flag & RC_REPMBUF)
245 m_freem(rp->rc_reply);
246 if (rp->rc_flag & RC_NAM)
247 FREE(rp->rc_nam, M_SONAME);
248 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
249 }
250 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
251 rp->rc_state = RC_INPROG;
252 rp->rc_xid = nd->nd_retxid;
253 saddr = (struct sockaddr_in *)nd->nd_nam;
254 switch (saddr->sin_family) {
255 case AF_INET:
256 rp->rc_flag |= RC_INETADDR;
257 rp->rc_inetaddr = saddr->sin_addr.s_addr;
258 break;
259 /* case AF_INET6: */
260 /* case AF_ISO: */
261 default:
262 /*
263 * XXXRW: Seems like we should only set RC_NAM if we
264 * actually manage to set rc_nam to something non-NULL.
265 */
266 rp->rc_flag |= RC_NAM;
267 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
268 break;
269 };
270 rp->rc_proc = nd->nd_procnum;
271 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
272 rp->rc_flag &= ~RC_LOCKED;
273 if (rp->rc_flag & RC_WANTED) {
274 rp->rc_flag &= ~RC_WANTED;
275 wakeup(rp);
276 }
277 return (RC_DOIT);
278 }
279
280 /*
281 * Update a request cache entry after the rpc has been done
282 */
283 void
284 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
285 {
286 struct nfsrvcache *rp;
287
288 NFSD_LOCK_ASSERT();
289
290 if (!nd->nd_nam2)
291 return;
292 loop:
293 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
294 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
295 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
296 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
297 if ((rp->rc_flag & RC_LOCKED) != 0) {
298 rp->rc_flag |= RC_WANTED;
299 (void) msleep(rp, &nfsd_mtx, PZERO-1,
300 "nfsrc", 0);
301 goto loop;
302 }
303 rp->rc_flag |= RC_LOCKED;
304 if (rp->rc_state == RC_DONE) {
305 /*
306 * This can occur if the cache is too small.
307 * Retransmits of the same request aren't
308 * dropped so we may see the operation
309 * complete more then once.
310 */
311 if (rp->rc_flag & RC_REPMBUF) {
312 m_freem(rp->rc_reply);
313 rp->rc_flag &= ~RC_REPMBUF;
314 }
315 }
316 rp->rc_state = RC_DONE;
317 /*
318 * If we have a valid reply update status and save
319 * the reply for non-idempotent rpc's.
320 */
321 if (repvalid && nonidempotent[nd->nd_procnum]) {
322 if ((nd->nd_flag & ND_NFSV3) == 0 &&
323 nfsv2_repstat[
324 nfsrvv2_procid[nd->nd_procnum]]) {
325 rp->rc_status = nd->nd_repstat;
326 rp->rc_flag |= RC_REPSTATUS;
327 } else {
328 NFSD_UNLOCK();
329 rp->rc_reply = m_copym(repmbuf,
330 0, M_COPYALL, M_TRYWAIT);
331 NFSD_LOCK();
332 rp->rc_flag |= RC_REPMBUF;
333 }
334 }
335 rp->rc_flag &= ~RC_LOCKED;
336 if (rp->rc_flag & RC_WANTED) {
337 rp->rc_flag &= ~RC_WANTED;
338 wakeup(rp);
339 }
340 return;
341 }
342 }
343 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
344 }
345
346 /*
347 * Clean out the cache. Called when the last nfsd terminates.
348 */
349 void
350 nfsrv_cleancache(void)
351 {
352 struct nfsrvcache *rp, *nextrp;
353
354 NFSD_LOCK_ASSERT();
355
356 for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
357 nextrp = TAILQ_NEXT(rp, rc_lru);
358 LIST_REMOVE(rp, rc_hash);
359 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
360 if (rp->rc_flag & RC_REPMBUF)
361 m_freem(rp->rc_reply);
362 if (rp->rc_flag & RC_NAM)
363 free(rp->rc_nam, M_SONAME);
364 free(rp, M_NFSD);
365 }
366 numnfsrvcache = 0;
367 }
Cache object: 9b018b94ea5241d3adb62967501a6224
|