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