]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_srvcache.c
xnu-792.21.3.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
CommitLineData
1c79356b 1/*
5d5c5d0d
A
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
8f6c56a5
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.
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
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
68#ifndef NFS_NOSERVER
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>
83#include <sys/socketvar.h> /* for dup_sockaddr */
91447636 84#include <libkern/OSAtomic.h>
1c79356b
A
85
86#include <netinet/in.h>
87#if ISO
88#include <netiso/iso.h>
89#endif
90#include <nfs/rpcv2.h>
91#include <nfs/nfsproto.h>
92#include <nfs/nfs.h>
93#include <nfs/nfsrvcache.h>
94
95extern struct nfsstats nfsstats;
96extern int nfsv2_procid[NFS_NPROCS];
97long numnfsrvcache;
98static long desirednfsrvcache = NFSRVCACHESIZ;
99
100#define NFSRCHASH(xid) \
101 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
102LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
103TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
104u_long nfsrvhash;
105
91447636
A
106lck_grp_t *nfsrv_reqcache_lck_grp;
107lck_grp_attr_t *nfsrv_reqcache_lck_grp_attr;
108lck_attr_t *nfsrv_reqcache_lck_attr;
109lck_mtx_t *nfsrv_reqcache_mutex;
1c79356b
A
110
111#define NETFAMILY(rp) \
112 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
113
114/*
115 * Static array that defines which nfs rpc's are nonidempotent
116 */
117static int nonidempotent[NFS_NPROCS] = {
118 FALSE,
119 FALSE,
120 TRUE,
121 FALSE,
122 FALSE,
123 FALSE,
124 FALSE,
125 TRUE,
126 TRUE,
127 TRUE,
128 TRUE,
129 TRUE,
130 TRUE,
131 TRUE,
132 TRUE,
133 TRUE,
134 FALSE,
135 FALSE,
136 FALSE,
137 FALSE,
138 FALSE,
139 FALSE,
140 FALSE,
1c79356b
A
141};
142
143/* True iff the rpc reply is an nfs status ONLY! */
144static int nfsv2_repstat[NFS_NPROCS] = {
145 FALSE,
146 FALSE,
147 FALSE,
148 FALSE,
149 FALSE,
150 FALSE,
151 FALSE,
152 FALSE,
153 FALSE,
154 FALSE,
155 TRUE,
156 TRUE,
157 TRUE,
158 TRUE,
159 FALSE,
160 TRUE,
161 FALSE,
162 FALSE,
163};
164
165/*
166 * Initialize the server request cache list
167 */
168void
169nfsrv_initcache()
170{
91447636
A
171 /* init nfs server request cache mutex */
172 nfsrv_reqcache_lck_grp_attr = lck_grp_attr_alloc_init();
21362eb3 173 lck_grp_attr_setstat(nfsrv_reqcache_lck_grp_attr);
91447636
A
174 nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", nfsrv_reqcache_lck_grp_attr);
175 nfsrv_reqcache_lck_attr = lck_attr_alloc_init();
176 nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, nfsrv_reqcache_lck_attr);
1c79356b
A
177
178 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
179 TAILQ_INIT(&nfsrvlruhead);
180}
181
182/*
183 * Look for the request in the cache
184 * If found then
185 * return action and optionally reply
186 * else
187 * insert it in the cache
188 *
189 * The rules are as follows:
190 * - if in progress, return DROP request
191 * - if completed within DELAY of the current time, return DROP it
192 * - if completed a longer time ago return REPLY if the reply was cached or
193 * return DOIT
194 * Update/add new request at end of lru list
195 */
196int
197nfsrv_getcache(nd, slp, repp)
91447636 198 struct nfsrv_descript *nd;
1c79356b 199 struct nfssvc_sock *slp;
91447636 200 mbuf_t *repp;
1c79356b 201{
91447636
A
202 struct nfsrvcache *rp;
203 mbuf_t mb;
1c79356b
A
204 struct sockaddr_in *saddr;
205 caddr_t bpos;
91447636 206 int ret, error;
1c79356b
A
207
208 /*
209 * Don't cache recent requests for reliable transport protocols.
210 * (Maybe we should for the case of a reconnect, but..)
211 */
212 if (!nd->nd_nam2)
213 return (RC_DOIT);
91447636 214 lck_mtx_lock(nfsrv_reqcache_mutex);
1c79356b
A
215loop:
216 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
217 rp = rp->rc_hash.le_next) {
218 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
219 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
1c79356b
A
220 if ((rp->rc_flag & RC_LOCKED) != 0) {
221 rp->rc_flag |= RC_WANTED;
222 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
223 goto loop;
224 }
225 rp->rc_flag |= RC_LOCKED;
226 /* If not at end of LRU chain, move it there */
227 if (rp->rc_lru.tqe_next) {
228 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
229 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
230 }
231 if (rp->rc_state == RC_UNUSED)
232 panic("nfsrv cache");
233 if (rp->rc_state == RC_INPROG) {
91447636 234 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_inproghits);
1c79356b
A
235 ret = RC_DROPIT;
236 } else if (rp->rc_flag & RC_REPSTATUS) {
91447636
A
237 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
238 nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb, &bpos);
1c79356b
A
239 ret = RC_REPLY;
240 } else if (rp->rc_flag & RC_REPMBUF) {
91447636
A
241 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
242 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, repp);
243 if (error) {
244 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
245 ret = RC_DROPIT;
246 } else {
247 ret = RC_REPLY;
248 }
1c79356b 249 } else {
91447636 250 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_idemdonehits);
1c79356b
A
251 rp->rc_state = RC_INPROG;
252 ret = RC_DOIT;
253 }
254 rp->rc_flag &= ~RC_LOCKED;
255 if (rp->rc_flag & RC_WANTED) {
256 rp->rc_flag &= ~RC_WANTED;
257 wakeup((caddr_t)rp);
258 }
91447636 259 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
260 return (ret);
261 }
262 }
91447636 263 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_misses);
1c79356b 264 if (numnfsrvcache < desirednfsrvcache) {
91447636 265 /* try to allocate a new entry */
1c79356b 266 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
91447636
A
267 if (rp) {
268 bzero((char *)rp, sizeof *rp);
269 numnfsrvcache++;
270 rp->rc_flag = RC_LOCKED;
271 }
1c79356b 272 } else {
91447636
A
273 rp = NULL;
274 }
275 if (!rp) {
276 /* try to reuse the least recently used entry */
1c79356b 277 rp = nfsrvlruhead.tqh_first;
91447636
A
278 if (!rp) {
279 /* no entry to reuse? */
280 /* OK, we just won't be able to cache this request */
281 lck_mtx_unlock(nfsrv_reqcache_mutex);
282 return (RC_DOIT);
283 }
1c79356b
A
284 while ((rp->rc_flag & RC_LOCKED) != 0) {
285 rp->rc_flag |= RC_WANTED;
286 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
287 rp = nfsrvlruhead.tqh_first;
288 }
289 rp->rc_flag |= RC_LOCKED;
290 LIST_REMOVE(rp, rc_hash);
291 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
292 if (rp->rc_flag & RC_REPMBUF)
91447636 293 mbuf_freem(rp->rc_reply);
1c79356b 294 if (rp->rc_flag & RC_NAM)
91447636 295 mbuf_freem(rp->rc_nam);
1c79356b
A
296 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
297 }
298 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
299 rp->rc_state = RC_INPROG;
300 rp->rc_xid = nd->nd_retxid;
91447636 301 saddr = mbuf_data(nd->nd_nam);
1c79356b
A
302 switch (saddr->sin_family) {
303 case AF_INET:
304 rp->rc_flag |= RC_INETADDR;
305 rp->rc_inetaddr = saddr->sin_addr.s_addr;
306 break;
307 case AF_ISO:
308 default:
91447636
A
309 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
310 if (error)
311 printf("nfsrv cache: nam copym failed\n");
312 else
313 rp->rc_flag |= RC_NAM;
1c79356b
A
314 break;
315 };
316 rp->rc_proc = nd->nd_procnum;
317 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
318 rp->rc_flag &= ~RC_LOCKED;
319 if (rp->rc_flag & RC_WANTED) {
320 rp->rc_flag &= ~RC_WANTED;
321 wakeup((caddr_t)rp);
322 }
91447636 323 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
324 return (RC_DOIT);
325}
326
327/*
328 * Update a request cache entry after the rpc has been done
329 */
330void
331nfsrv_updatecache(nd, repvalid, repmbuf)
91447636 332 struct nfsrv_descript *nd;
1c79356b 333 int repvalid;
91447636 334 mbuf_t repmbuf;
1c79356b 335{
91447636
A
336 struct nfsrvcache *rp;
337 int error;
1c79356b
A
338
339 if (!nd->nd_nam2)
340 return;
91447636 341 lck_mtx_lock(nfsrv_reqcache_mutex);
1c79356b
A
342loop:
343 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
344 rp = rp->rc_hash.le_next) {
345 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
346 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
1c79356b
A
347 if ((rp->rc_flag & RC_LOCKED) != 0) {
348 rp->rc_flag |= RC_WANTED;
349 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
350 goto loop;
351 }
352 rp->rc_flag |= RC_LOCKED;
353 rp->rc_state = RC_DONE;
354 /*
355 * If we have a valid reply update status and save
356 * the reply for non-idempotent rpc's.
357 */
358 if (repvalid && nonidempotent[nd->nd_procnum]) {
359 if ((nd->nd_flag & ND_NFSV3) == 0 &&
360 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
361 rp->rc_status = nd->nd_repstat;
362 rp->rc_flag |= RC_REPSTATUS;
363 } else {
91447636
A
364 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
365 if (!error)
366 rp->rc_flag |= RC_REPMBUF;
1c79356b
A
367 }
368 }
369 rp->rc_flag &= ~RC_LOCKED;
370 if (rp->rc_flag & RC_WANTED) {
371 rp->rc_flag &= ~RC_WANTED;
372 wakeup((caddr_t)rp);
373 }
91447636 374 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
375 return;
376 }
377 }
91447636 378 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
379}
380
381/*
382 * Clean out the cache. Called when the last nfsd terminates.
383 */
384void
385nfsrv_cleancache()
386{
91447636 387 struct nfsrvcache *rp, *nextrp;
1c79356b 388
91447636 389 lck_mtx_lock(nfsrv_reqcache_mutex);
1c79356b
A
390 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
391 nextrp = rp->rc_lru.tqe_next;
392 LIST_REMOVE(rp, rc_hash);
393 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
394 _FREE(rp, M_NFSD);
395 }
396 numnfsrvcache = 0;
91447636 397 lck_mtx_unlock(nfsrv_reqcache_mutex);
1c79356b
A
398}
399
400#endif /* NFS_NOSERVER */