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