]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
c89c0ca987107268e4ee33ceb835be85e491fd7e
[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 nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", nfsrv_reqcache_lck_grp_attr);
174 nfsrv_reqcache_lck_attr = lck_attr_alloc_init();
175 nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, nfsrv_reqcache_lck_attr);
176
177 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
178 TAILQ_INIT(&nfsrvlruhead);
179 }
180
181 /*
182 * Look for the request in the cache
183 * If found then
184 * return action and optionally reply
185 * else
186 * insert it in the cache
187 *
188 * The rules are as follows:
189 * - if in progress, return DROP request
190 * - if completed within DELAY of the current time, return DROP it
191 * - if completed a longer time ago return REPLY if the reply was cached or
192 * return DOIT
193 * Update/add new request at end of lru list
194 */
195 int
196 nfsrv_getcache(nd, slp, repp)
197 struct nfsrv_descript *nd;
198 struct nfssvc_sock *slp;
199 mbuf_t *repp;
200 {
201 struct nfsrvcache *rp;
202 mbuf_t mb;
203 struct sockaddr_in *saddr;
204 caddr_t bpos;
205 int ret, error;
206
207 /*
208 * Don't cache recent requests for reliable transport protocols.
209 * (Maybe we should for the case of a reconnect, but..)
210 */
211 if (!nd->nd_nam2)
212 return (RC_DOIT);
213 lck_mtx_lock(nfsrv_reqcache_mutex);
214 loop:
215 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
216 rp = rp->rc_hash.le_next) {
217 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
218 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
219 if ((rp->rc_flag & RC_LOCKED) != 0) {
220 rp->rc_flag |= RC_WANTED;
221 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
222 goto loop;
223 }
224 rp->rc_flag |= RC_LOCKED;
225 /* If not at end of LRU chain, move it there */
226 if (rp->rc_lru.tqe_next) {
227 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
228 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
229 }
230 if (rp->rc_state == RC_UNUSED)
231 panic("nfsrv cache");
232 if (rp->rc_state == RC_INPROG) {
233 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_inproghits);
234 ret = RC_DROPIT;
235 } else if (rp->rc_flag & RC_REPSTATUS) {
236 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
237 nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb, &bpos);
238 ret = RC_REPLY;
239 } else if (rp->rc_flag & RC_REPMBUF) {
240 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
241 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, repp);
242 if (error) {
243 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
244 ret = RC_DROPIT;
245 } else {
246 ret = RC_REPLY;
247 }
248 } else {
249 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_idemdonehits);
250 rp->rc_state = RC_INPROG;
251 ret = RC_DOIT;
252 }
253 rp->rc_flag &= ~RC_LOCKED;
254 if (rp->rc_flag & RC_WANTED) {
255 rp->rc_flag &= ~RC_WANTED;
256 wakeup((caddr_t)rp);
257 }
258 lck_mtx_unlock(nfsrv_reqcache_mutex);
259 return (ret);
260 }
261 }
262 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_misses);
263 if (numnfsrvcache < desirednfsrvcache) {
264 /* try to allocate a new entry */
265 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
266 if (rp) {
267 bzero((char *)rp, sizeof *rp);
268 numnfsrvcache++;
269 rp->rc_flag = RC_LOCKED;
270 }
271 } else {
272 rp = NULL;
273 }
274 if (!rp) {
275 /* try to reuse the least recently used entry */
276 rp = nfsrvlruhead.tqh_first;
277 if (!rp) {
278 /* no entry to reuse? */
279 /* OK, we just won't be able to cache this request */
280 lck_mtx_unlock(nfsrv_reqcache_mutex);
281 return (RC_DOIT);
282 }
283 while ((rp->rc_flag & RC_LOCKED) != 0) {
284 rp->rc_flag |= RC_WANTED;
285 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
286 rp = nfsrvlruhead.tqh_first;
287 }
288 rp->rc_flag |= RC_LOCKED;
289 LIST_REMOVE(rp, rc_hash);
290 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
291 if (rp->rc_flag & RC_REPMBUF)
292 mbuf_freem(rp->rc_reply);
293 if (rp->rc_flag & RC_NAM)
294 mbuf_freem(rp->rc_nam);
295 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
296 }
297 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
298 rp->rc_state = RC_INPROG;
299 rp->rc_xid = nd->nd_retxid;
300 saddr = mbuf_data(nd->nd_nam);
301 switch (saddr->sin_family) {
302 case AF_INET:
303 rp->rc_flag |= RC_INETADDR;
304 rp->rc_inetaddr = saddr->sin_addr.s_addr;
305 break;
306 case AF_ISO:
307 default:
308 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
309 if (error)
310 printf("nfsrv cache: nam copym failed\n");
311 else
312 rp->rc_flag |= RC_NAM;
313 break;
314 };
315 rp->rc_proc = nd->nd_procnum;
316 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
317 rp->rc_flag &= ~RC_LOCKED;
318 if (rp->rc_flag & RC_WANTED) {
319 rp->rc_flag &= ~RC_WANTED;
320 wakeup((caddr_t)rp);
321 }
322 lck_mtx_unlock(nfsrv_reqcache_mutex);
323 return (RC_DOIT);
324 }
325
326 /*
327 * Update a request cache entry after the rpc has been done
328 */
329 void
330 nfsrv_updatecache(nd, repvalid, repmbuf)
331 struct nfsrv_descript *nd;
332 int repvalid;
333 mbuf_t repmbuf;
334 {
335 struct nfsrvcache *rp;
336 int error;
337
338 if (!nd->nd_nam2)
339 return;
340 lck_mtx_lock(nfsrv_reqcache_mutex);
341 loop:
342 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
343 rp = rp->rc_hash.le_next) {
344 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
345 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
346 if ((rp->rc_flag & RC_LOCKED) != 0) {
347 rp->rc_flag |= RC_WANTED;
348 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
349 goto loop;
350 }
351 rp->rc_flag |= RC_LOCKED;
352 rp->rc_state = RC_DONE;
353 /*
354 * If we have a valid reply update status and save
355 * the reply for non-idempotent rpc's.
356 */
357 if (repvalid && nonidempotent[nd->nd_procnum]) {
358 if ((nd->nd_flag & ND_NFSV3) == 0 &&
359 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
360 rp->rc_status = nd->nd_repstat;
361 rp->rc_flag |= RC_REPSTATUS;
362 } else {
363 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
364 if (!error)
365 rp->rc_flag |= RC_REPMBUF;
366 }
367 }
368 rp->rc_flag &= ~RC_LOCKED;
369 if (rp->rc_flag & RC_WANTED) {
370 rp->rc_flag &= ~RC_WANTED;
371 wakeup((caddr_t)rp);
372 }
373 lck_mtx_unlock(nfsrv_reqcache_mutex);
374 return;
375 }
376 }
377 lck_mtx_unlock(nfsrv_reqcache_mutex);
378 }
379
380 /*
381 * Clean out the cache. Called when the last nfsd terminates.
382 */
383 void
384 nfsrv_cleancache()
385 {
386 struct nfsrvcache *rp, *nextrp;
387
388 lck_mtx_lock(nfsrv_reqcache_mutex);
389 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
390 nextrp = rp->rc_lru.tqe_next;
391 LIST_REMOVE(rp, rc_hash);
392 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
393 _FREE(rp, M_NFSD);
394 }
395 numnfsrvcache = 0;
396 lck_mtx_unlock(nfsrv_reqcache_mutex);
397 }
398
399 #endif /* NFS_NOSERVER */