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 #include <sys/eventhandler.h>
53
54 #include <netinet/in.h>
55 #include <nfs/rpcv2.h>
56 #include <nfs/nfsproto.h>
57 #include <nfsserver/nfs.h>
58 #include <nfsserver/nfsrvcache.h>
59
60 static long numnfsrvcache;
61 static long desirednfsrvcache;
62
63 #define NFSRCHASH(xid) \
64 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
65 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
66 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
67 static u_long nfsrvhash;
68
69 #define TRUE 1
70 #define FALSE 0
71
72 #define NETFAMILY(rp) \
73 (((rp)->rc_flag & RC_NAM) ? (rp)->rc_nam->sa_family : AF_INET)
74
75 /*
76 * Static array that defines which nfs rpc's are nonidempotent
77 */
78 static const int nonidempotent[NFS_NPROCS] = {
79 FALSE,
80 FALSE,
81 TRUE,
82 FALSE,
83 FALSE,
84 FALSE,
85 FALSE,
86 TRUE,
87 TRUE,
88 TRUE,
89 TRUE,
90 TRUE,
91 TRUE,
92 TRUE,
93 TRUE,
94 TRUE,
95 FALSE,
96 FALSE,
97 FALSE,
98 FALSE,
99 FALSE,
100 FALSE,
101 FALSE,
102 };
103
104 /* True iff the rpc reply is an nfs status ONLY! */
105 static const int nfsv2_repstat[NFS_NPROCS] = {
106 FALSE,
107 FALSE,
108 FALSE,
109 FALSE,
110 FALSE,
111 FALSE,
112 FALSE,
113 FALSE,
114 FALSE,
115 FALSE,
116 TRUE,
117 TRUE,
118 TRUE,
119 TRUE,
120 FALSE,
121 TRUE,
122 FALSE,
123 FALSE,
124 };
125
126 /*
127 * Size the NFS server's duplicate request cache at 1/2 the nmbclsters, floating
128 * within a (64, 2048) range. This is to prevent all mbuf clusters being tied up
129 * in the NFS dupreq cache for small values of nmbclusters.
130 */
131 static void
132 nfsrvcache_size_change(void *tag)
133 {
134 desirednfsrvcache = nmbclusters /2;
135 if (desirednfsrvcache > NFSRVCACHE_MAX_SIZE)
136 desirednfsrvcache = NFSRVCACHE_MAX_SIZE;
137 if (desirednfsrvcache < NFSRVCACHE_MIN_SIZE)
138 desirednfsrvcache = NFSRVCACHE_MIN_SIZE;
139 }
140
141 /*
142 * Initialize the server request cache list
143 */
144 void
145 nfsrv_initcache(void)
146 {
147 nfsrvcache_size_change(NULL);
148 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
149 TAILQ_INIT(&nfsrvlruhead);
150 EVENTHANDLER_REGISTER(nmbclusters_change, nfsrvcache_size_change, NULL,
151 EVENTHANDLER_PRI_FIRST);
152 }
153
154 /*
155 * Teardown the server request cache list
156 */
157 void
158 nfsrv_destroycache(void)
159 {
160
161 KASSERT(TAILQ_EMPTY(&nfsrvlruhead), ("%s: pending requests", __func__));
162 hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash);
163 }
164
165 /*
166 * Look for the request in the cache
167 * If found then
168 * return action and optionally reply
169 * else
170 * insert it in the cache
171 *
172 * The rules are as follows:
173 * - if in progress, return DROP request
174 * - if completed within DELAY of the current time, return DROP it
175 * - if completed a longer time ago return REPLY if the reply was cached or
176 * return DOIT
177 * Update/add new request at end of lru list
178 */
179 int
180 nfsrv_getcache(struct nfsrv_descript *nd, struct mbuf **repp)
181 {
182 struct nfsrvcache *rp;
183 struct mbuf *mb;
184 struct sockaddr_in *saddr;
185 caddr_t bpos;
186 int ret;
187
188 NFSD_LOCK_ASSERT();
189
190 /*
191 * Don't cache recent requests for reliable transport protocols.
192 * (Maybe we should for the case of a reconnect, but..)
193 */
194 if (!nd->nd_nam2)
195 return (RC_DOIT);
196 loop:
197 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
198 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
199 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
200 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff));
201 if ((rp->rc_flag & RC_LOCKED) != 0) {
202 rp->rc_flag |= RC_WANTED;
203 (void) msleep(rp, &nfsd_mtx, PZERO-1,
204 "nfsrc", 0);
205 goto loop;
206 }
207 rp->rc_flag |= RC_LOCKED;
208 /* If not at end of LRU chain, move it there */
209 if (TAILQ_NEXT(rp, rc_lru)) {
210 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
211 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
212 }
213 if (rp->rc_state == RC_UNUSED)
214 panic("nfsrv cache");
215 if (rp->rc_state == RC_INPROG) {
216 nfsrvstats.srvcache_inproghits++;
217 ret = RC_DROPIT;
218 } else if (rp->rc_flag & RC_REPSTATUS) {
219 nfsrvstats.srvcache_nonidemdonehits++;
220 *repp = nfs_rephead(0, nd, rp->rc_status,
221 &mb, &bpos);
222 ret = RC_REPLY;
223 } else if (rp->rc_flag & RC_REPMBUF) {
224 nfsrvstats.srvcache_nonidemdonehits++;
225 NFSD_UNLOCK();
226 *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
227 M_TRYWAIT);
228 NFSD_LOCK();
229 ret = RC_REPLY;
230 } else {
231 nfsrvstats.srvcache_idemdonehits++;
232 rp->rc_state = RC_INPROG;
233 ret = RC_DOIT;
234 }
235 rp->rc_flag &= ~RC_LOCKED;
236 if (rp->rc_flag & RC_WANTED) {
237 rp->rc_flag &= ~RC_WANTED;
238 wakeup(rp);
239 }
240 return (ret);
241 }
242 }
243 nfsrvstats.srvcache_misses++;
244 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff));
245 if (numnfsrvcache < desirednfsrvcache) {
246 NFSD_UNLOCK();
247 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
248 M_NFSD, M_WAITOK | M_ZERO);
249 NFSD_LOCK();
250 numnfsrvcache++;
251 rp->rc_flag = RC_LOCKED;
252 } else {
253 rp = TAILQ_FIRST(&nfsrvlruhead);
254 while ((rp->rc_flag & RC_LOCKED) != 0) {
255 rp->rc_flag |= RC_WANTED;
256 (void) msleep(rp, &nfsd_mtx, PZERO-1, "nfsrc", 0);
257 rp = TAILQ_FIRST(&nfsrvlruhead);
258 }
259 rp->rc_flag |= RC_LOCKED;
260 LIST_REMOVE(rp, rc_hash);
261 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
262 if (rp->rc_flag & RC_REPMBUF)
263 m_freem(rp->rc_reply);
264 if (rp->rc_flag & RC_NAM)
265 FREE(rp->rc_nam, M_SONAME);
266 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
267 }
268 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
269 rp->rc_state = RC_INPROG;
270 rp->rc_xid = nd->nd_retxid;
271 saddr = (struct sockaddr_in *)nd->nd_nam;
272 switch (saddr->sin_family) {
273 case AF_INET:
274 rp->rc_flag |= RC_INETADDR;
275 rp->rc_inetaddr = saddr->sin_addr.s_addr;
276 break;
277 /* case AF_INET6: */
278 /* case AF_ISO: */
279 default:
280 /*
281 * XXXRW: Seems like we should only set RC_NAM if we
282 * actually manage to set rc_nam to something non-NULL.
283 */
284 rp->rc_flag |= RC_NAM;
285 rp->rc_nam = sodupsockaddr(nd->nd_nam, M_NOWAIT);
286 break;
287 };
288 rp->rc_proc = nd->nd_procnum;
289 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
290 rp->rc_flag &= ~RC_LOCKED;
291 if (rp->rc_flag & RC_WANTED) {
292 rp->rc_flag &= ~RC_WANTED;
293 wakeup(rp);
294 }
295 return (RC_DOIT);
296 }
297
298 /*
299 * Update a request cache entry after the rpc has been done
300 */
301 void
302 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
303 {
304 struct nfsrvcache *rp;
305
306 NFSD_LOCK_ASSERT();
307
308 if (!nd->nd_nam2)
309 return;
310 loop:
311 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
312 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
313 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
314 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff));
315 if ((rp->rc_flag & RC_LOCKED) != 0) {
316 rp->rc_flag |= RC_WANTED;
317 (void) msleep(rp, &nfsd_mtx, PZERO-1,
318 "nfsrc", 0);
319 goto loop;
320 }
321 rp->rc_flag |= RC_LOCKED;
322 if (rp->rc_state == RC_DONE) {
323 /*
324 * This can occur if the cache is too small.
325 * Retransmits of the same request aren't
326 * dropped so we may see the operation
327 * complete more then once.
328 */
329 if (rp->rc_flag & RC_REPMBUF) {
330 m_freem(rp->rc_reply);
331 rp->rc_flag &= ~RC_REPMBUF;
332 }
333 }
334 rp->rc_state = RC_DONE;
335 /*
336 * If we have a valid reply update status and save
337 * the reply for non-idempotent rpc's.
338 */
339 if (repvalid && nonidempotent[nd->nd_procnum]) {
340 if ((nd->nd_flag & ND_NFSV3) == 0 &&
341 nfsv2_repstat[
342 nfsrvv2_procid[nd->nd_procnum]]) {
343 rp->rc_status = nd->nd_repstat;
344 rp->rc_flag |= RC_REPSTATUS;
345 } else {
346 NFSD_UNLOCK();
347 rp->rc_reply = m_copym(repmbuf,
348 0, M_COPYALL, M_TRYWAIT);
349 NFSD_LOCK();
350 rp->rc_flag |= RC_REPMBUF;
351 }
352 }
353 rp->rc_flag &= ~RC_LOCKED;
354 if (rp->rc_flag & RC_WANTED) {
355 rp->rc_flag &= ~RC_WANTED;
356 wakeup(rp);
357 }
358 return;
359 }
360 }
361 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff));
362 }
363
364 /*
365 * Clean out the cache. Called when the last nfsd terminates.
366 */
367 void
368 nfsrv_cleancache(void)
369 {
370 struct nfsrvcache *rp, *nextrp;
371
372 NFSD_LOCK_ASSERT();
373
374 for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) {
375 nextrp = TAILQ_NEXT(rp, rc_lru);
376 LIST_REMOVE(rp, rc_hash);
377 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
378 if (rp->rc_flag & RC_REPMBUF)
379 m_freem(rp->rc_reply);
380 if (rp->rc_flag & RC_NAM)
381 free(rp->rc_nam, M_SONAME);
382 free(rp, M_NFSD);
383 }
384 numnfsrvcache = 0;
385 }
Cache object: efc9950ddc9472b2ceb1bcc2c5e2d930
|