]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
1 /*
2 * Copyright (c) 2000-2010 Apple 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 #if NFSSERVER
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 <libkern/OSAtomic.h>
84
85 #include <netinet/in.h>
86 #include <nfs/rpcv2.h>
87 #include <nfs/nfsproto.h>
88 #include <nfs/nfs.h>
89 #include <nfs/nfsrvcache.h>
90
91 extern int nfsv2_procid[NFS_NPROCS];
92 static int nfsrv_reqcache_count;
93 int nfsrv_reqcache_size = NFSRVCACHESIZ;
94
95 #define NFSRCHASH(xid) \
96 (&nfsrv_reqcache_hashtbl[((xid) + ((xid) >> 24)) & nfsrv_reqcache_hash])
97 LIST_HEAD(nfsrv_reqcache_hash, nfsrvcache) * nfsrv_reqcache_hashtbl;
98 TAILQ_HEAD(nfsrv_reqcache_lru, nfsrvcache) nfsrv_reqcache_lruhead;
99 u_long nfsrv_reqcache_hash;
100
101 lck_grp_t *nfsrv_reqcache_lck_grp;
102 lck_mtx_t *nfsrv_reqcache_mutex;
103
104 /*
105 * Static array that defines which nfs rpc's are nonidempotent
106 */
107 static int nonidempotent[NFS_NPROCS] = {
108 FALSE,
109 FALSE,
110 TRUE,
111 FALSE,
112 FALSE,
113 FALSE,
114 FALSE,
115 TRUE,
116 TRUE,
117 TRUE,
118 TRUE,
119 TRUE,
120 TRUE,
121 TRUE,
122 TRUE,
123 TRUE,
124 FALSE,
125 FALSE,
126 FALSE,
127 FALSE,
128 FALSE,
129 FALSE,
130 FALSE,
131 };
132
133 /* True iff the rpc reply is an nfs status ONLY! */
134 static int nfsv2_repstat[NFS_NPROCS] = {
135 FALSE,
136 FALSE,
137 FALSE,
138 FALSE,
139 FALSE,
140 FALSE,
141 FALSE,
142 FALSE,
143 FALSE,
144 FALSE,
145 TRUE,
146 TRUE,
147 TRUE,
148 TRUE,
149 FALSE,
150 TRUE,
151 FALSE,
152 FALSE,
153 };
154
155 /*
156 * Initialize the server request cache list
157 */
158 void
159 nfsrv_initcache(void)
160 {
161 if (nfsrv_reqcache_size <= 0) {
162 return;
163 }
164
165 lck_mtx_lock(nfsrv_reqcache_mutex);
166 /* init nfs server request cache hash table */
167 nfsrv_reqcache_hashtbl = hashinit(nfsrv_reqcache_size, M_NFSD, &nfsrv_reqcache_hash);
168 TAILQ_INIT(&nfsrv_reqcache_lruhead);
169 lck_mtx_unlock(nfsrv_reqcache_mutex);
170 }
171
172 /*
173 * This function compares two net addresses by family and returns TRUE
174 * if they are the same host.
175 * If there is any doubt, return FALSE.
176 * The AF_INET family is handled as a special case so that address mbufs
177 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
178 * Ditto for AF_INET6 which is only 16 bytes.
179 */
180 static int
181 netaddr_match(
182 int family,
183 union nethostaddr *haddr,
184 mbuf_t nam)
185 {
186 struct sockaddr_in *inetaddr;
187 struct sockaddr_in6 *inet6addr;
188
189 switch (family) {
190 case AF_INET:
191 inetaddr = mbuf_data(nam);
192 if ((inetaddr->sin_family == AF_INET) &&
193 (inetaddr->sin_addr.s_addr == haddr->had_inetaddr)) {
194 return 1;
195 }
196 break;
197 case AF_INET6:
198 inet6addr = mbuf_data(nam);
199 if ((inet6addr->sin6_family == AF_INET6) &&
200 !bcmp(&inet6addr->sin6_addr, &haddr->had_inet6addr, sizeof(inet6addr->sin6_addr))) {
201 return 1;
202 }
203 break;
204 }
205 return 0;
206 }
207
208 /*
209 * Look for the request in the cache
210 * If found then
211 * return action and optionally reply
212 * else
213 * insert it in the cache
214 *
215 * The rules are as follows:
216 * - if in progress, return DROP request
217 * - if completed within DELAY of the current time, return DROP it
218 * - if completed a longer time ago return REPLY if the reply was cached or
219 * return DOIT
220 * Update/add new request at end of lru list
221 */
222 int
223 nfsrv_getcache(
224 struct nfsrv_descript *nd,
225 struct nfsrv_sock *slp,
226 mbuf_t *mrepp)
227 {
228 struct nfsrvcache *rp;
229 struct nfsm_chain nmrep;
230 struct sockaddr *saddr;
231 int ret, error;
232
233 /*
234 * Don't cache recent requests for reliable transport protocols.
235 * (Maybe we should for the case of a reconnect, but..)
236 */
237 if (!nd->nd_nam2) {
238 return RC_DOIT;
239 }
240 lck_mtx_lock(nfsrv_reqcache_mutex);
241 loop:
242 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
243 rp = rp->rc_hash.le_next) {
244 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
245 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
246 if ((rp->rc_flag & RC_LOCKED) != 0) {
247 rp->rc_flag |= RC_WANTED;
248 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
249 goto loop;
250 }
251 rp->rc_flag |= RC_LOCKED;
252 /* If not at end of LRU chain, move it there */
253 if (rp->rc_lru.tqe_next) {
254 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
255 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
256 }
257 if (rp->rc_state == RC_UNUSED) {
258 panic("nfsrv cache");
259 }
260 if (rp->rc_state == RC_INPROG) {
261 OSAddAtomic64(1, &nfsstats.srvcache_inproghits);
262 ret = RC_DROPIT;
263 } else if (rp->rc_flag & RC_REPSTATUS) {
264 OSAddAtomic64(1, &nfsstats.srvcache_nonidemdonehits);
265 nd->nd_repstat = rp->rc_status;
266 error = nfsrv_rephead(nd, slp, &nmrep, 0);
267 if (error) {
268 printf("nfsrv cache: reply alloc failed for nonidem request hit\n");
269 ret = RC_DROPIT;
270 *mrepp = NULL;
271 } else {
272 ret = RC_REPLY;
273 *mrepp = nmrep.nmc_mhead;
274 }
275 } else if (rp->rc_flag & RC_REPMBUF) {
276 OSAddAtomic64(1, &nfsstats.srvcache_nonidemdonehits);
277 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, mrepp);
278 if (error) {
279 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
280 ret = RC_DROPIT;
281 } else {
282 ret = RC_REPLY;
283 }
284 } else {
285 OSAddAtomic64(1, &nfsstats.srvcache_idemdonehits);
286 rp->rc_state = RC_INPROG;
287 ret = RC_DOIT;
288 }
289 rp->rc_flag &= ~RC_LOCKED;
290 if (rp->rc_flag & RC_WANTED) {
291 rp->rc_flag &= ~RC_WANTED;
292 wakeup(rp);
293 }
294 lck_mtx_unlock(nfsrv_reqcache_mutex);
295 return ret;
296 }
297 }
298 OSAddAtomic64(1, &nfsstats.srvcache_misses);
299 if (nfsrv_reqcache_count < nfsrv_reqcache_size) {
300 /* try to allocate a new entry */
301 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
302 if (rp) {
303 bzero((char *)rp, sizeof *rp);
304 nfsrv_reqcache_count++;
305 rp->rc_flag = RC_LOCKED;
306 }
307 } else {
308 rp = NULL;
309 }
310 if (!rp) {
311 /* try to reuse the least recently used entry */
312 rp = nfsrv_reqcache_lruhead.tqh_first;
313 if (!rp) {
314 /* no entry to reuse? */
315 /* OK, we just won't be able to cache this request */
316 lck_mtx_unlock(nfsrv_reqcache_mutex);
317 return RC_DOIT;
318 }
319 while ((rp->rc_flag & RC_LOCKED) != 0) {
320 rp->rc_flag |= RC_WANTED;
321 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
322 rp = nfsrv_reqcache_lruhead.tqh_first;
323 }
324 rp->rc_flag |= RC_LOCKED;
325 LIST_REMOVE(rp, rc_hash);
326 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
327 if (rp->rc_flag & RC_REPMBUF) {
328 mbuf_freem(rp->rc_reply);
329 }
330 if (rp->rc_flag & RC_NAM) {
331 mbuf_freem(rp->rc_nam);
332 }
333 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
334 }
335 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
336 rp->rc_state = RC_INPROG;
337 rp->rc_xid = nd->nd_retxid;
338 saddr = mbuf_data(nd->nd_nam);
339 rp->rc_family = saddr->sa_family;
340 switch (saddr->sa_family) {
341 case AF_INET:
342 rp->rc_flag |= RC_INETADDR;
343 rp->rc_inetaddr = ((struct sockaddr_in*)saddr)->sin_addr.s_addr;
344 break;
345 case AF_INET6:
346 rp->rc_flag |= RC_INETADDR;
347 rp->rc_inet6addr = ((struct sockaddr_in6*)saddr)->sin6_addr;
348 break;
349 default:
350 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
351 if (error) {
352 printf("nfsrv cache: nam copym failed\n");
353 } else {
354 rp->rc_flag |= RC_NAM;
355 }
356 break;
357 }
358 ;
359 rp->rc_proc = nd->nd_procnum;
360 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
361 rp->rc_flag &= ~RC_LOCKED;
362 if (rp->rc_flag & RC_WANTED) {
363 rp->rc_flag &= ~RC_WANTED;
364 wakeup(rp);
365 }
366 lck_mtx_unlock(nfsrv_reqcache_mutex);
367 return RC_DOIT;
368 }
369
370 /*
371 * Update a request cache entry after the rpc has been done
372 */
373 void
374 nfsrv_updatecache(
375 struct nfsrv_descript *nd,
376 int repvalid,
377 mbuf_t repmbuf)
378 {
379 struct nfsrvcache *rp;
380 int error;
381
382 if (!nd->nd_nam2) {
383 return;
384 }
385 lck_mtx_lock(nfsrv_reqcache_mutex);
386 loop:
387 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
388 rp = rp->rc_hash.le_next) {
389 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
390 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
391 if ((rp->rc_flag & RC_LOCKED) != 0) {
392 rp->rc_flag |= RC_WANTED;
393 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
394 goto loop;
395 }
396 rp->rc_flag |= RC_LOCKED;
397 if (rp->rc_state == RC_DONE) {
398 /*
399 * This can occur if the cache is too small.
400 * Retransmits of the same request aren't
401 * dropped so we may see the operation
402 * complete more then once.
403 */
404 if (rp->rc_flag & RC_REPMBUF) {
405 mbuf_freem(rp->rc_reply);
406 rp->rc_flag &= ~RC_REPMBUF;
407 }
408 }
409 rp->rc_state = RC_DONE;
410 /*
411 * If we have a valid reply update status and save
412 * the reply for non-idempotent rpc's.
413 */
414 if (repvalid && nonidempotent[nd->nd_procnum]) {
415 if ((nd->nd_vers == NFS_VER2) &&
416 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
417 rp->rc_status = nd->nd_repstat;
418 rp->rc_flag |= RC_REPSTATUS;
419 } else {
420 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
421 if (!error) {
422 rp->rc_flag |= RC_REPMBUF;
423 }
424 }
425 }
426 rp->rc_flag &= ~RC_LOCKED;
427 if (rp->rc_flag & RC_WANTED) {
428 rp->rc_flag &= ~RC_WANTED;
429 wakeup(rp);
430 }
431 lck_mtx_unlock(nfsrv_reqcache_mutex);
432 return;
433 }
434 }
435 lck_mtx_unlock(nfsrv_reqcache_mutex);
436 }
437
438 /*
439 * Clean out the cache. Called when the last nfsd terminates.
440 */
441 void
442 nfsrv_cleancache(void)
443 {
444 struct nfsrvcache *rp, *nextrp;
445
446 lck_mtx_lock(nfsrv_reqcache_mutex);
447 for (rp = nfsrv_reqcache_lruhead.tqh_first; rp != 0; rp = nextrp) {
448 nextrp = rp->rc_lru.tqe_next;
449 LIST_REMOVE(rp, rc_hash);
450 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
451 _FREE(rp, M_NFSD);
452 }
453 nfsrv_reqcache_count = 0;
454 FREE(nfsrv_reqcache_hashtbl, M_TEMP);
455 lck_mtx_unlock(nfsrv_reqcache_mutex);
456 }
457
458 #endif /* NFSSERVER */