]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_srvcache.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
CommitLineData
1c79356b 1/*
6d2010ae 2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
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
2d21ac55 68#if NFSSERVER
1c79356b
A
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>
91447636 76#include <sys/mount_internal.h>
1c79356b
A
77#include <sys/kernel.h>
78#include <sys/systm.h>
79#include <sys/proc.h>
91447636 80#include <sys/kpi_mbuf.h>
1c79356b
A
81#include <sys/malloc.h>
82#include <sys/socket.h>
91447636 83#include <libkern/OSAtomic.h>
1c79356b
A
84
85#include <netinet/in.h>
1c79356b
A
86#include <nfs/rpcv2.h>
87#include <nfs/nfsproto.h>
88#include <nfs/nfs.h>
89#include <nfs/nfsrvcache.h>
90
1c79356b 91extern int nfsv2_procid[NFS_NPROCS];
2d21ac55
A
92static int nfsrv_reqcache_count;
93int nfsrv_reqcache_size = NFSRVCACHESIZ;
1c79356b 94
0a7de745 95#define NFSRCHASH(xid) \
2d21ac55 96 (&nfsrv_reqcache_hashtbl[((xid) + ((xid) >> 24)) & nfsrv_reqcache_hash])
0a7de745 97LIST_HEAD(nfsrv_reqcache_hash, nfsrvcache) * nfsrv_reqcache_hashtbl;
2d21ac55
A
98TAILQ_HEAD(nfsrv_reqcache_lru, nfsrvcache) nfsrv_reqcache_lruhead;
99u_long nfsrv_reqcache_hash;
1c79356b 100
91447636 101lck_grp_t *nfsrv_reqcache_lck_grp;
91447636 102lck_mtx_t *nfsrv_reqcache_mutex;
1c79356b 103
1c79356b
A
104/*
105 * Static array that defines which nfs rpc's are nonidempotent
106 */
107static 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,
1c79356b
A
131};
132
133/* True iff the rpc reply is an nfs status ONLY! */
134static 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 */
158void
2d21ac55
A
159nfsrv_initcache(void)
160{
0a7de745 161 if (nfsrv_reqcache_size <= 0) {
2d21ac55 162 return;
0a7de745 163 }
2d21ac55
A
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.
6d2010ae 178 * Ditto for AF_INET6 which is only 16 bytes.
2d21ac55
A
179 */
180static int
181netaddr_match(
182 int family,
183 union nethostaddr *haddr,
184 mbuf_t nam)
1c79356b 185{
2d21ac55 186 struct sockaddr_in *inetaddr;
6d2010ae 187 struct sockaddr_in6 *inet6addr;
1c79356b 188
2d21ac55
A
189 switch (family) {
190 case AF_INET:
191 inetaddr = mbuf_data(nam);
6d2010ae 192 if ((inetaddr->sin_family == AF_INET) &&
0a7de745
A
193 (inetaddr->sin_addr.s_addr == haddr->had_inetaddr)) {
194 return 1;
195 }
2d21ac55 196 break;
6d2010ae
A
197 case AF_INET6:
198 inet6addr = mbuf_data(nam);
199 if ((inet6addr->sin6_family == AF_INET6) &&
0a7de745
A
200 !bcmp(&inet6addr->sin6_addr, &haddr->had_inet6addr, sizeof(inet6addr->sin6_addr))) {
201 return 1;
202 }
2d21ac55 203 break;
6d2010ae 204 }
0a7de745 205 return 0;
1c79356b
A
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 */
222int
2d21ac55
A
223nfsrv_getcache(
224 struct nfsrv_descript *nd,
225 struct nfsrv_sock *slp,
226 mbuf_t *mrepp)
1c79356b 227{
91447636 228 struct nfsrvcache *rp;
2d21ac55 229 struct nfsm_chain nmrep;
6d2010ae 230 struct sockaddr *saddr;
91447636 231 int ret, error;
1c79356b
A
232
233 /*
234 * Don't cache recent requests for reliable transport protocols.
235 * (Maybe we should for the case of a reconnect, but..)
236 */
0a7de745
A
237 if (!nd->nd_nam2) {
238 return RC_DOIT;
239 }
91447636 240 lck_mtx_lock(nfsrv_reqcache_mutex);
1c79356b
A
241loop:
242 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
243 rp = rp->rc_hash.le_next) {
0a7de745
A
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)) {
1c79356b
A
246 if ((rp->rc_flag & RC_LOCKED) != 0) {
247 rp->rc_flag |= RC_WANTED;
0a7de745 248 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
1c79356b
A
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) {
2d21ac55
A
254 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
255 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
1c79356b 256 }
0a7de745 257 if (rp->rc_state == RC_UNUSED) {
1c79356b 258 panic("nfsrv cache");
0a7de745 259 }
1c79356b 260 if (rp->rc_state == RC_INPROG) {
316670eb 261 OSAddAtomic64(1, &nfsstats.srvcache_inproghits);
1c79356b
A
262 ret = RC_DROPIT;
263 } else if (rp->rc_flag & RC_REPSTATUS) {
316670eb 264 OSAddAtomic64(1, &nfsstats.srvcache_nonidemdonehits);
2d21ac55
A
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 }
1c79356b 275 } else if (rp->rc_flag & RC_REPMBUF) {
316670eb 276 OSAddAtomic64(1, &nfsstats.srvcache_nonidemdonehits);
2d21ac55 277 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, mrepp);
91447636
A
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 }
1c79356b 284 } else {
316670eb 285 OSAddAtomic64(1, &nfsstats.srvcache_idemdonehits);
1c79356b
A
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;
2d21ac55 292 wakeup(rp);
1c79356b 293 }
91447636 294 lck_mtx_unlock(nfsrv_reqcache_mutex);
0a7de745 295 return ret;
1c79356b
A
296 }
297 }
316670eb 298 OSAddAtomic64(1, &nfsstats.srvcache_misses);
2d21ac55 299 if (nfsrv_reqcache_count < nfsrv_reqcache_size) {
91447636 300 /* try to allocate a new entry */
1c79356b 301 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
91447636
A
302 if (rp) {
303 bzero((char *)rp, sizeof *rp);
2d21ac55 304 nfsrv_reqcache_count++;
91447636
A
305 rp->rc_flag = RC_LOCKED;
306 }
1c79356b 307 } else {
91447636
A
308 rp = NULL;
309 }
310 if (!rp) {
311 /* try to reuse the least recently used entry */
2d21ac55 312 rp = nfsrv_reqcache_lruhead.tqh_first;
91447636
A
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);
0a7de745 317 return RC_DOIT;
91447636 318 }
1c79356b
A
319 while ((rp->rc_flag & RC_LOCKED) != 0) {
320 rp->rc_flag |= RC_WANTED;
0a7de745 321 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
2d21ac55 322 rp = nfsrv_reqcache_lruhead.tqh_first;
1c79356b
A
323 }
324 rp->rc_flag |= RC_LOCKED;
325 LIST_REMOVE(rp, rc_hash);
2d21ac55 326 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
0a7de745 327 if (rp->rc_flag & RC_REPMBUF) {
91447636 328 mbuf_freem(rp->rc_reply);
0a7de745
A
329 }
330 if (rp->rc_flag & RC_NAM) {
91447636 331 mbuf_freem(rp->rc_nam);
0a7de745 332 }
1c79356b
A
333 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
334 }
2d21ac55 335 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
1c79356b
A
336 rp->rc_state = RC_INPROG;
337 rp->rc_xid = nd->nd_retxid;
91447636 338 saddr = mbuf_data(nd->nd_nam);
6d2010ae
A
339 rp->rc_family = saddr->sa_family;
340 switch (saddr->sa_family) {
1c79356b
A
341 case AF_INET:
342 rp->rc_flag |= RC_INETADDR;
6d2010ae
A
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;
1c79356b 348 break;
1c79356b 349 default:
91447636 350 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
0a7de745 351 if (error) {
91447636 352 printf("nfsrv cache: nam copym failed\n");
0a7de745 353 } else {
91447636 354 rp->rc_flag |= RC_NAM;
0a7de745 355 }
1c79356b 356 break;
0a7de745
A
357 }
358 ;
1c79356b
A
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;
2d21ac55 364 wakeup(rp);
1c79356b 365 }
91447636 366 lck_mtx_unlock(nfsrv_reqcache_mutex);
0a7de745 367 return RC_DOIT;
1c79356b
A
368}
369
370/*
371 * Update a request cache entry after the rpc has been done
372 */
373void
2d21ac55
A
374nfsrv_updatecache(
375 struct nfsrv_descript *nd,
376 int repvalid,
377 mbuf_t repmbuf)
1c79356b 378{
91447636
A
379 struct nfsrvcache *rp;
380 int error;
1c79356b 381
0a7de745 382 if (!nd->nd_nam2) {
1c79356b 383 return;
0a7de745 384 }
91447636 385 lck_mtx_lock(nfsrv_reqcache_mutex);
1c79356b
A
386loop:
387 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
388 rp = rp->rc_hash.le_next) {
0a7de745
A
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)) {
1c79356b
A
391 if ((rp->rc_flag & RC_LOCKED) != 0) {
392 rp->rc_flag |= RC_WANTED;
0a7de745 393 msleep(rp, nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
1c79356b
A
394 goto loop;
395 }
396 rp->rc_flag |= RC_LOCKED;
0a7de745
A
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 }
2d21ac55 408 }
1c79356b
A
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]) {
2d21ac55 415 if ((nd->nd_vers == NFS_VER2) &&
0a7de745 416 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
1c79356b
A
417 rp->rc_status = nd->nd_repstat;
418 rp->rc_flag |= RC_REPSTATUS;
419 } else {
91447636 420 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
0a7de745 421 if (!error) {
91447636 422 rp->rc_flag |= RC_REPMBUF;
0a7de745 423 }
1c79356b
A
424 }
425 }
426 rp->rc_flag &= ~RC_LOCKED;
427 if (rp->rc_flag & RC_WANTED) {
428 rp->rc_flag &= ~RC_WANTED;
2d21ac55 429 wakeup(rp);
1c79356b 430 }
91447636 431 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
432 return;
433 }
434 }
91447636 435 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
436}
437
438/*
439 * Clean out the cache. Called when the last nfsd terminates.
440 */
441void
2d21ac55 442nfsrv_cleancache(void)
1c79356b 443{
91447636 444 struct nfsrvcache *rp, *nextrp;
1c79356b 445
91447636 446 lck_mtx_lock(nfsrv_reqcache_mutex);
2d21ac55 447 for (rp = nfsrv_reqcache_lruhead.tqh_first; rp != 0; rp = nextrp) {
1c79356b
A
448 nextrp = rp->rc_lru.tqe_next;
449 LIST_REMOVE(rp, rc_hash);
2d21ac55 450 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
1c79356b
A
451 _FREE(rp, M_NFSD);
452 }
2d21ac55
A
453 nfsrv_reqcache_count = 0;
454 FREE(nfsrv_reqcache_hashtbl, M_TEMP);
91447636 455 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
456}
457
2d21ac55 458#endif /* NFSSERVER */