]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
e1ae2104e2f4209880f26b117b18387d450e6746
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*
32 * Copyright (c) 1989, 1993
33 * The Regents of the University of California. All rights reserved.
34 *
35 * This code is derived from software contributed to Berkeley by
36 * Rick Macklem at The University of Guelph.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95
67 * FreeBSD-Id: nfs_srvcache.c,v 1.15 1997/10/12 20:25:46 phk Exp $
68 */
69
70 #ifndef NFS_NOSERVER
71 /*
72 * Reference: Chet Juszczak, "Improving the Performance and Correctness
73 * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
74 * pages 53-63. San Diego, February 1989.
75 */
76 #include <sys/param.h>
77 #include <sys/vnode.h>
78 #include <sys/mount_internal.h>
79 #include <sys/kernel.h>
80 #include <sys/systm.h>
81 #include <sys/proc.h>
82 #include <sys/kpi_mbuf.h>
83 #include <sys/malloc.h>
84 #include <sys/socket.h>
85 #include <sys/socketvar.h> /* for dup_sockaddr */
86 #include <libkern/OSAtomic.h>
87
88 #include <netinet/in.h>
89 #if ISO
90 #include <netiso/iso.h>
91 #endif
92 #include <nfs/rpcv2.h>
93 #include <nfs/nfsproto.h>
94 #include <nfs/nfs.h>
95 #include <nfs/nfsrvcache.h>
96
97 extern struct nfsstats nfsstats;
98 extern int nfsv2_procid[NFS_NPROCS];
99 long numnfsrvcache;
100 static long desirednfsrvcache = NFSRVCACHESIZ;
101
102 #define NFSRCHASH(xid) \
103 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
104 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
105 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
106 u_long nfsrvhash;
107
108 lck_grp_t *nfsrv_reqcache_lck_grp;
109 lck_grp_attr_t *nfsrv_reqcache_lck_grp_attr;
110 lck_attr_t *nfsrv_reqcache_lck_attr;
111 lck_mtx_t *nfsrv_reqcache_mutex;
112
113 #define NETFAMILY(rp) \
114 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
115
116 /*
117 * Static array that defines which nfs rpc's are nonidempotent
118 */
119 static int nonidempotent[NFS_NPROCS] = {
120 FALSE,
121 FALSE,
122 TRUE,
123 FALSE,
124 FALSE,
125 FALSE,
126 FALSE,
127 TRUE,
128 TRUE,
129 TRUE,
130 TRUE,
131 TRUE,
132 TRUE,
133 TRUE,
134 TRUE,
135 TRUE,
136 FALSE,
137 FALSE,
138 FALSE,
139 FALSE,
140 FALSE,
141 FALSE,
142 FALSE,
143 };
144
145 /* True iff the rpc reply is an nfs status ONLY! */
146 static int nfsv2_repstat[NFS_NPROCS] = {
147 FALSE,
148 FALSE,
149 FALSE,
150 FALSE,
151 FALSE,
152 FALSE,
153 FALSE,
154 FALSE,
155 FALSE,
156 FALSE,
157 TRUE,
158 TRUE,
159 TRUE,
160 TRUE,
161 FALSE,
162 TRUE,
163 FALSE,
164 FALSE,
165 };
166
167 /*
168 * Initialize the server request cache list
169 */
170 void
171 nfsrv_initcache()
172 {
173 /* init nfs server request cache mutex */
174 nfsrv_reqcache_lck_grp_attr = lck_grp_attr_alloc_init();
175 nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", nfsrv_reqcache_lck_grp_attr);
176 nfsrv_reqcache_lck_attr = lck_attr_alloc_init();
177 nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, nfsrv_reqcache_lck_attr);
178
179 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
180 TAILQ_INIT(&nfsrvlruhead);
181 }
182
183 /*
184 * Look for the request in the cache
185 * If found then
186 * return action and optionally reply
187 * else
188 * insert it in the cache
189 *
190 * The rules are as follows:
191 * - if in progress, return DROP request
192 * - if completed within DELAY of the current time, return DROP it
193 * - if completed a longer time ago return REPLY if the reply was cached or
194 * return DOIT
195 * Update/add new request at end of lru list
196 */
197 int
198 nfsrv_getcache(nd, slp, repp)
199 struct nfsrv_descript *nd;
200 struct nfssvc_sock *slp;
201 mbuf_t *repp;
202 {
203 struct nfsrvcache *rp;
204 mbuf_t mb;
205 struct sockaddr_in *saddr;
206 caddr_t bpos;
207 int ret, error;
208
209 /*
210 * Don't cache recent requests for reliable transport protocols.
211 * (Maybe we should for the case of a reconnect, but..)
212 */
213 if (!nd->nd_nam2)
214 return (RC_DOIT);
215 lck_mtx_lock(nfsrv_reqcache_mutex);
216 loop:
217 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
218 rp = rp->rc_hash.le_next) {
219 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
220 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
221 if ((rp->rc_flag & RC_LOCKED) != 0) {
222 rp->rc_flag |= RC_WANTED;
223 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
224 goto loop;
225 }
226 rp->rc_flag |= RC_LOCKED;
227 /* If not at end of LRU chain, move it there */
228 if (rp->rc_lru.tqe_next) {
229 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
230 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
231 }
232 if (rp->rc_state == RC_UNUSED)
233 panic("nfsrv cache");
234 if (rp->rc_state == RC_INPROG) {
235 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_inproghits);
236 ret = RC_DROPIT;
237 } else if (rp->rc_flag & RC_REPSTATUS) {
238 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
239 nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb, &bpos);
240 ret = RC_REPLY;
241 } else if (rp->rc_flag & RC_REPMBUF) {
242 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_nonidemdonehits);
243 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, repp);
244 if (error) {
245 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
246 ret = RC_DROPIT;
247 } else {
248 ret = RC_REPLY;
249 }
250 } else {
251 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_idemdonehits);
252 rp->rc_state = RC_INPROG;
253 ret = RC_DOIT;
254 }
255 rp->rc_flag &= ~RC_LOCKED;
256 if (rp->rc_flag & RC_WANTED) {
257 rp->rc_flag &= ~RC_WANTED;
258 wakeup((caddr_t)rp);
259 }
260 lck_mtx_unlock(nfsrv_reqcache_mutex);
261 return (ret);
262 }
263 }
264 OSAddAtomic(1, (SInt32*)&nfsstats.srvcache_misses);
265 if (numnfsrvcache < desirednfsrvcache) {
266 /* try to allocate a new entry */
267 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
268 if (rp) {
269 bzero((char *)rp, sizeof *rp);
270 numnfsrvcache++;
271 rp->rc_flag = RC_LOCKED;
272 }
273 } else {
274 rp = NULL;
275 }
276 if (!rp) {
277 /* try to reuse the least recently used entry */
278 rp = nfsrvlruhead.tqh_first;
279 if (!rp) {
280 /* no entry to reuse? */
281 /* OK, we just won't be able to cache this request */
282 lck_mtx_unlock(nfsrv_reqcache_mutex);
283 return (RC_DOIT);
284 }
285 while ((rp->rc_flag & RC_LOCKED) != 0) {
286 rp->rc_flag |= RC_WANTED;
287 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
288 rp = nfsrvlruhead.tqh_first;
289 }
290 rp->rc_flag |= RC_LOCKED;
291 LIST_REMOVE(rp, rc_hash);
292 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
293 if (rp->rc_flag & RC_REPMBUF)
294 mbuf_freem(rp->rc_reply);
295 if (rp->rc_flag & RC_NAM)
296 mbuf_freem(rp->rc_nam);
297 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
298 }
299 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
300 rp->rc_state = RC_INPROG;
301 rp->rc_xid = nd->nd_retxid;
302 saddr = mbuf_data(nd->nd_nam);
303 switch (saddr->sin_family) {
304 case AF_INET:
305 rp->rc_flag |= RC_INETADDR;
306 rp->rc_inetaddr = saddr->sin_addr.s_addr;
307 break;
308 case AF_ISO:
309 default:
310 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
311 if (error)
312 printf("nfsrv cache: nam copym failed\n");
313 else
314 rp->rc_flag |= RC_NAM;
315 break;
316 };
317 rp->rc_proc = nd->nd_procnum;
318 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
319 rp->rc_flag &= ~RC_LOCKED;
320 if (rp->rc_flag & RC_WANTED) {
321 rp->rc_flag &= ~RC_WANTED;
322 wakeup((caddr_t)rp);
323 }
324 lck_mtx_unlock(nfsrv_reqcache_mutex);
325 return (RC_DOIT);
326 }
327
328 /*
329 * Update a request cache entry after the rpc has been done
330 */
331 void
332 nfsrv_updatecache(nd, repvalid, repmbuf)
333 struct nfsrv_descript *nd;
334 int repvalid;
335 mbuf_t repmbuf;
336 {
337 struct nfsrvcache *rp;
338 int error;
339
340 if (!nd->nd_nam2)
341 return;
342 lck_mtx_lock(nfsrv_reqcache_mutex);
343 loop:
344 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
345 rp = rp->rc_hash.le_next) {
346 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
347 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
348 if ((rp->rc_flag & RC_LOCKED) != 0) {
349 rp->rc_flag |= RC_WANTED;
350 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
351 goto loop;
352 }
353 rp->rc_flag |= RC_LOCKED;
354 rp->rc_state = RC_DONE;
355 /*
356 * If we have a valid reply update status and save
357 * the reply for non-idempotent rpc's.
358 */
359 if (repvalid && nonidempotent[nd->nd_procnum]) {
360 if ((nd->nd_flag & ND_NFSV3) == 0 &&
361 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
362 rp->rc_status = nd->nd_repstat;
363 rp->rc_flag |= RC_REPSTATUS;
364 } else {
365 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
366 if (!error)
367 rp->rc_flag |= RC_REPMBUF;
368 }
369 }
370 rp->rc_flag &= ~RC_LOCKED;
371 if (rp->rc_flag & RC_WANTED) {
372 rp->rc_flag &= ~RC_WANTED;
373 wakeup((caddr_t)rp);
374 }
375 lck_mtx_unlock(nfsrv_reqcache_mutex);
376 return;
377 }
378 }
379 lck_mtx_unlock(nfsrv_reqcache_mutex);
380 }
381
382 /*
383 * Clean out the cache. Called when the last nfsd terminates.
384 */
385 void
386 nfsrv_cleancache()
387 {
388 struct nfsrvcache *rp, *nextrp;
389
390 lck_mtx_lock(nfsrv_reqcache_mutex);
391 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
392 nextrp = rp->rc_lru.tqe_next;
393 LIST_REMOVE(rp, rc_hash);
394 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
395 _FREE(rp, M_NFSD);
396 }
397 numnfsrvcache = 0;
398 lck_mtx_unlock(nfsrv_reqcache_mutex);
399 }
400
401 #endif /* NFS_NOSERVER */