]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
xnu-792.21.3.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95
65 * FreeBSD-Id: nfs_srvcache.c,v 1.15 1997/10/12 20:25:46 phk Exp $
66 */
67
68 #ifndef NFS_NOSERVER
69 /*
70 * Reference: Chet Juszczak, "Improving the Performance and Correctness
71 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
72 * pages 53-63. San Diego, February 1989.
73 */
74 #include <sys/param.h>
75 #include <sys/vnode.h>
76 #include <sys/mount_internal.h>
77 #include <sys/kernel.h>
78 #include <sys/systm.h>
79 #include <sys/proc.h>
80 #include <sys/kpi_mbuf.h>
81 #include <sys/malloc.h>
82 #include <sys/socket.h>
83 #include <sys/socketvar.h> /* for dup_sockaddr */
84 #include <libkern/OSAtomic.h>
85
86 #include <netinet/in.h>
87 #if ISO
88 #include <netiso/iso.h>
89 #endif
90 #include <nfs/rpcv2.h>
91 #include <nfs/nfsproto.h>
92 #include <nfs/nfs.h>
93 #include <nfs/nfsrvcache.h>
94
95 extern struct nfsstats nfsstats;
96 extern int nfsv2_procid[NFS_NPROCS];
97 long numnfsrvcache;
98 static long desirednfsrvcache = NFSRVCACHESIZ;
99
100 #define NFSRCHASH(xid) \
101 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
102 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
103 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
104 u_long nfsrvhash;
105
106 lck_grp_t *nfsrv_reqcache_lck_grp;
107 lck_grp_attr_t *nfsrv_reqcache_lck_grp_attr;
108 lck_attr_t *nfsrv_reqcache_lck_attr;
109 lck_mtx_t *nfsrv_reqcache_mutex;
110
111 #define NETFAMILY(rp) \
112 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
113
114 /*
115 * Static array that defines which nfs rpc's are nonidempotent
116 */
117 static int nonidempotent[NFS_NPROCS] = {
118 FALSE,
119 FALSE,
120 TRUE,
121 FALSE,
122 FALSE,
123 FALSE,
124 FALSE,
125 TRUE,
126 TRUE,
127 TRUE,
128 TRUE,
129 TRUE,
130 TRUE,
131 TRUE,
132 TRUE,
133 TRUE,
134 FALSE,
135 FALSE,
136 FALSE,
137 FALSE,
138 FALSE,
139 FALSE,
140 FALSE,
141 };
142
143 /* True iff the rpc reply is an nfs status ONLY! */
144 static int nfsv2_repstat[NFS_NPROCS] = {
145 FALSE,
146 FALSE,
147 FALSE,
148 FALSE,
149 FALSE,
150 FALSE,
151 FALSE,
152 FALSE,
153 FALSE,
154 FALSE,
155 TRUE,
156 TRUE,
157 TRUE,
158 TRUE,
159 FALSE,
160 TRUE,
161 FALSE,
162 FALSE,
163 };
164
165 /*
166 * Initialize the server request cache list
167 */
168 void
169 nfsrv_initcache()
170 {
171 /* init nfs server request cache mutex */
172 nfsrv_reqcache_lck_grp_attr = lck_grp_attr_alloc_init();
173 lck_grp_attr_setstat(nfsrv_reqcache_lck_grp_attr);
174 nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", nfsrv_reqcache_lck_grp_attr);
175 nfsrv_reqcache_lck_attr = lck_attr_alloc_init();
176 nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, nfsrv_reqcache_lck_attr);
177
178 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
179 TAILQ_INIT(&nfsrvlruhead);
180 }
181
182 /*
183 * Look for the request in the cache
184 * If found then
185 * return action and optionally reply
186 * else
187 * insert it in the cache
188 *
189 * The rules are as follows:
190 * - if in progress, return DROP request
191 * - if completed within DELAY of the current time, return DROP it
192 * - if completed a longer time ago return REPLY if the reply was cached or
193 * return DOIT
194 * Update/add new request at end of lru list
195 */
196 int
197 nfsrv_getcache(nd, slp, repp)
198 struct nfsrv_descript *nd;
199 struct nfssvc_sock *slp;
200 mbuf_t *repp;
201 {
202 struct nfsrvcache *rp;
203 mbuf_t mb;
204 struct sockaddr_in *saddr;
205 caddr_t bpos;
206 int ret, error;
207
208 /*
209 * Don't cache recent requests for reliable transport protocols.
210 * (Maybe we should for the case of a reconnect, but..)
211 */
212 if (!nd->nd_nam2)
213 return (RC_DOIT);
214 lck_mtx_lock(nfsrv_reqcache_mutex);
215 loop:
216 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
217 rp = rp->rc_hash.le_next) {
218 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
219 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
220 if ((rp->rc_flag & RC_LOCKED) != 0) {
221 rp->rc_flag |= RC_WANTED;
222 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
223 goto loop;
224 }
225 rp->rc_flag |= RC_LOCKED;
226 /* If not at end of LRU chain, move it there */
227 if (rp->rc_lru.tqe_next) {
228 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
229 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
230 }
231 if (rp->rc_state == RC_UNUSED)
232 panic("nfsrv cache");
233 if (rp->rc_state == RC_INPROG) {
234 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_inproghits);
235 ret = RC_DROPIT;
236 } else if (rp->rc_flag & RC_REPSTATUS) {
237 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
238 nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb, &bpos);
239 ret = RC_REPLY;
240 } else if (rp->rc_flag & RC_REPMBUF) {
241 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
242 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, repp);
243 if (error) {
244 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
245 ret = RC_DROPIT;
246 } else {
247 ret = RC_REPLY;
248 }
249 } else {
250 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_idemdonehits);
251 rp->rc_state = RC_INPROG;
252 ret = RC_DOIT;
253 }
254 rp->rc_flag &= ~RC_LOCKED;
255 if (rp->rc_flag & RC_WANTED) {
256 rp->rc_flag &= ~RC_WANTED;
257 wakeup((caddr_t)rp);
258 }
259 lck_mtx_unlock(nfsrv_reqcache_mutex);
260 return (ret);
261 }
262 }
263 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_misses);
264 if (numnfsrvcache < desirednfsrvcache) {
265 /* try to allocate a new entry */
266 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
267 if (rp) {
268 bzero((char *)rp, sizeof *rp);
269 numnfsrvcache++;
270 rp->rc_flag = RC_LOCKED;
271 }
272 } else {
273 rp = NULL;
274 }
275 if (!rp) {
276 /* try to reuse the least recently used entry */
277 rp = nfsrvlruhead.tqh_first;
278 if (!rp) {
279 /* no entry to reuse? */
280 /* OK, we just won't be able to cache this request */
281 lck_mtx_unlock(nfsrv_reqcache_mutex);
282 return (RC_DOIT);
283 }
284 while ((rp->rc_flag & RC_LOCKED) != 0) {
285 rp->rc_flag |= RC_WANTED;
286 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
287 rp = nfsrvlruhead.tqh_first;
288 }
289 rp->rc_flag |= RC_LOCKED;
290 LIST_REMOVE(rp, rc_hash);
291 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
292 if (rp->rc_flag & RC_REPMBUF)
293 mbuf_freem(rp->rc_reply);
294 if (rp->rc_flag & RC_NAM)
295 mbuf_freem(rp->rc_nam);
296 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
297 }
298 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
299 rp->rc_state = RC_INPROG;
300 rp->rc_xid = nd->nd_retxid;
301 saddr = mbuf_data(nd->nd_nam);
302 switch (saddr->sin_family) {
303 case AF_INET:
304 rp->rc_flag |= RC_INETADDR;
305 rp->rc_inetaddr = saddr->sin_addr.s_addr;
306 break;
307 case AF_ISO:
308 default:
309 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
310 if (error)
311 printf("nfsrv cache: nam copym failed\n");
312 else
313 rp->rc_flag |= RC_NAM;
314 break;
315 };
316 rp->rc_proc = nd->nd_procnum;
317 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
318 rp->rc_flag &= ~RC_LOCKED;
319 if (rp->rc_flag & RC_WANTED) {
320 rp->rc_flag &= ~RC_WANTED;
321 wakeup((caddr_t)rp);
322 }
323 lck_mtx_unlock(nfsrv_reqcache_mutex);
324 return (RC_DOIT);
325 }
326
327 /*
328 * Update a request cache entry after the rpc has been done
329 */
330 void
331 nfsrv_updatecache(nd, repvalid, repmbuf)
332 struct nfsrv_descript *nd;
333 int repvalid;
334 mbuf_t repmbuf;
335 {
336 struct nfsrvcache *rp;
337 int error;
338
339 if (!nd->nd_nam2)
340 return;
341 lck_mtx_lock(nfsrv_reqcache_mutex);
342 loop:
343 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
344 rp = rp->rc_hash.le_next) {
345 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
346 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
347 if ((rp->rc_flag & RC_LOCKED) != 0) {
348 rp->rc_flag |= RC_WANTED;
349 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
350 goto loop;
351 }
352 rp->rc_flag |= RC_LOCKED;
353 rp->rc_state = RC_DONE;
354 /*
355 * If we have a valid reply update status and save
356 * the reply for non-idempotent rpc's.
357 */
358 if (repvalid && nonidempotent[nd->nd_procnum]) {
359 if ((nd->nd_flag & ND_NFSV3) == 0 &&
360 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
361 rp->rc_status = nd->nd_repstat;
362 rp->rc_flag |= RC_REPSTATUS;
363 } else {
364 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
365 if (!error)
366 rp->rc_flag |= RC_REPMBUF;
367 }
368 }
369 rp->rc_flag &= ~RC_LOCKED;
370 if (rp->rc_flag & RC_WANTED) {
371 rp->rc_flag &= ~RC_WANTED;
372 wakeup((caddr_t)rp);
373 }
374 lck_mtx_unlock(nfsrv_reqcache_mutex);
375 return;
376 }
377 }
378 lck_mtx_unlock(nfsrv_reqcache_mutex);
379 }
380
381 /*
382 * Clean out the cache. Called when the last nfsd terminates.
383 */
384 void
385 nfsrv_cleancache()
386 {
387 struct nfsrvcache *rp, *nextrp;
388
389 lck_mtx_lock(nfsrv_reqcache_mutex);
390 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
391 nextrp = rp->rc_lru.tqe_next;
392 LIST_REMOVE(rp, rc_hash);
393 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
394 _FREE(rp, M_NFSD);
395 }
396 numnfsrvcache = 0;
397 lck_mtx_unlock(nfsrv_reqcache_mutex);
398 }
399
400 #endif /* NFS_NOSERVER */