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