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