]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
1 /*
2 * Copyright (c) 2000-2009 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_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 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
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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 #if NFSSERVER
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>
76 #include <sys/mount_internal.h>
77 #include <sys/kernel.h>
78 #include <sys/systm.h>
79 #include <sys/proc.h>
80 #include <sys/kpi_mbuf.h>
81 #include <sys/malloc.h>
82 #include <sys/socket.h>
83 #include <libkern/OSAtomic.h>
84
85 #include <netinet/in.h>
86 #include <nfs/rpcv2.h>
87 #include <nfs/nfsproto.h>
88 #include <nfs/nfs.h>
89 #include <nfs/nfsrvcache.h>
90
91 extern int nfsv2_procid[NFS_NPROCS];
92 static int nfsrv_reqcache_count;
93 int nfsrv_reqcache_size = NFSRVCACHESIZ;
94
95 #define NFSRCHASH(xid) \
96 (&nfsrv_reqcache_hashtbl[((xid) + ((xid) >> 24)) & nfsrv_reqcache_hash])
97 LIST_HEAD(nfsrv_reqcache_hash, nfsrvcache) *nfsrv_reqcache_hashtbl;
98 TAILQ_HEAD(nfsrv_reqcache_lru, nfsrvcache) nfsrv_reqcache_lruhead;
99 u_long nfsrv_reqcache_hash;
100
101 lck_grp_t *nfsrv_reqcache_lck_grp;
102 lck_mtx_t *nfsrv_reqcache_mutex;
103
104 /*
105 * Static array that defines which nfs rpc's are nonidempotent
106 */
107 static 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,
131 };
132
133 /* True iff the rpc reply is an nfs status ONLY! */
134 static 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 */
158 void
159 nfsrv_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 */
178 static int
179 netaddr_match(
180 int family,
181 union nethostaddr *haddr,
182 mbuf_t nam)
183 {
184 struct sockaddr_in *inetaddr;
185
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);
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 */
213 int
214 nfsrv_getcache(
215 struct nfsrv_descript *nd,
216 struct nfsrv_sock *slp,
217 mbuf_t *mrepp)
218 {
219 struct nfsrvcache *rp;
220 struct nfsm_chain nmrep;
221 struct sockaddr_in *saddr;
222 int ret, error;
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);
230 lck_mtx_lock(nfsrv_reqcache_mutex);
231 loop:
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 &&
235 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
236 if ((rp->rc_flag & RC_LOCKED) != 0) {
237 rp->rc_flag |= RC_WANTED;
238 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
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) {
244 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
245 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
246 }
247 if (rp->rc_state == RC_UNUSED)
248 panic("nfsrv cache");
249 if (rp->rc_state == RC_INPROG) {
250 OSAddAtomic(1, &nfsstats.srvcache_inproghits);
251 ret = RC_DROPIT;
252 } else if (rp->rc_flag & RC_REPSTATUS) {
253 OSAddAtomic(1, &nfsstats.srvcache_nonidemdonehits);
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 }
264 } else if (rp->rc_flag & RC_REPMBUF) {
265 OSAddAtomic(1, &nfsstats.srvcache_nonidemdonehits);
266 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, mrepp);
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 }
273 } else {
274 OSAddAtomic(1, &nfsstats.srvcache_idemdonehits);
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;
281 wakeup(rp);
282 }
283 lck_mtx_unlock(nfsrv_reqcache_mutex);
284 return (ret);
285 }
286 }
287 OSAddAtomic(1, &nfsstats.srvcache_misses);
288 if (nfsrv_reqcache_count < nfsrv_reqcache_size) {
289 /* try to allocate a new entry */
290 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
291 if (rp) {
292 bzero((char *)rp, sizeof *rp);
293 nfsrv_reqcache_count++;
294 rp->rc_flag = RC_LOCKED;
295 }
296 } else {
297 rp = NULL;
298 }
299 if (!rp) {
300 /* try to reuse the least recently used entry */
301 rp = nfsrv_reqcache_lruhead.tqh_first;
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 }
308 while ((rp->rc_flag & RC_LOCKED) != 0) {
309 rp->rc_flag |= RC_WANTED;
310 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
311 rp = nfsrv_reqcache_lruhead.tqh_first;
312 }
313 rp->rc_flag |= RC_LOCKED;
314 LIST_REMOVE(rp, rc_hash);
315 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
316 if (rp->rc_flag & RC_REPMBUF)
317 mbuf_freem(rp->rc_reply);
318 if (rp->rc_flag & RC_NAM)
319 mbuf_freem(rp->rc_nam);
320 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
321 }
322 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
323 rp->rc_state = RC_INPROG;
324 rp->rc_xid = nd->nd_retxid;
325 saddr = mbuf_data(nd->nd_nam);
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;
331 default:
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;
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;
344 wakeup(rp);
345 }
346 lck_mtx_unlock(nfsrv_reqcache_mutex);
347 return (RC_DOIT);
348 }
349
350 /*
351 * Update a request cache entry after the rpc has been done
352 */
353 void
354 nfsrv_updatecache(
355 struct nfsrv_descript *nd,
356 int repvalid,
357 mbuf_t repmbuf)
358 {
359 struct nfsrvcache *rp;
360 int error;
361
362 if (!nd->nd_nam2)
363 return;
364 lck_mtx_lock(nfsrv_reqcache_mutex);
365 loop:
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 &&
369 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) {
370 if ((rp->rc_flag & RC_LOCKED) != 0) {
371 rp->rc_flag |= RC_WANTED;
372 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
373 goto loop;
374 }
375 rp->rc_flag |= RC_LOCKED;
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 }
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]) {
394 if ((nd->nd_vers == NFS_VER2) &&
395 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
396 rp->rc_status = nd->nd_repstat;
397 rp->rc_flag |= RC_REPSTATUS;
398 } else {
399 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
400 if (!error)
401 rp->rc_flag |= RC_REPMBUF;
402 }
403 }
404 rp->rc_flag &= ~RC_LOCKED;
405 if (rp->rc_flag & RC_WANTED) {
406 rp->rc_flag &= ~RC_WANTED;
407 wakeup(rp);
408 }
409 lck_mtx_unlock(nfsrv_reqcache_mutex);
410 return;
411 }
412 }
413 lck_mtx_unlock(nfsrv_reqcache_mutex);
414 }
415
416 /*
417 * Clean out the cache. Called when the last nfsd terminates.
418 */
419 void
420 nfsrv_cleancache(void)
421 {
422 struct nfsrvcache *rp, *nextrp;
423
424 lck_mtx_lock(nfsrv_reqcache_mutex);
425 for (rp = nfsrv_reqcache_lruhead.tqh_first; rp != 0; rp = nextrp) {
426 nextrp = rp->rc_lru.tqe_next;
427 LIST_REMOVE(rp, rc_hash);
428 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
429 _FREE(rp, M_NFSD);
430 }
431 nfsrv_reqcache_count = 0;
432 FREE(nfsrv_reqcache_hashtbl, M_TEMP);
433 lck_mtx_unlock(nfsrv_reqcache_mutex);
434 }
435
436 #endif /* NFSSERVER */