]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_srvcache.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_srvcache.c
1 /*
2 * Copyright (c) 2000-2010 Apple 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 * Ditto for AF_INET6 which is only 16 bytes.
178 */
179 static int
180 netaddr_match(
181 int family,
182 union nethostaddr *haddr,
183 mbuf_t nam)
184 {
185 struct sockaddr_in *inetaddr;
186 struct sockaddr_in6 *inet6addr;
187
188 switch (family) {
189 case AF_INET:
190 inetaddr = mbuf_data(nam);
191 if ((inetaddr->sin_family == AF_INET) &&
192 (inetaddr->sin_addr.s_addr == haddr->had_inetaddr))
193 return (1);
194 break;
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);
200 break;
201 }
202 return (0);
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 */
219 int
220 nfsrv_getcache(
221 struct nfsrv_descript *nd,
222 struct nfsrv_sock *slp,
223 mbuf_t *mrepp)
224 {
225 struct nfsrvcache *rp;
226 struct nfsm_chain nmrep;
227 struct sockaddr *saddr;
228 int ret, error;
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);
236 lck_mtx_lock(nfsrv_reqcache_mutex);
237 loop:
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 &&
241 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
242 if ((rp->rc_flag & RC_LOCKED) != 0) {
243 rp->rc_flag |= RC_WANTED;
244 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
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) {
250 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
251 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
252 }
253 if (rp->rc_state == RC_UNUSED)
254 panic("nfsrv cache");
255 if (rp->rc_state == RC_INPROG) {
256 OSAddAtomic(1, &nfsstats.srvcache_inproghits);
257 ret = RC_DROPIT;
258 } else if (rp->rc_flag & RC_REPSTATUS) {
259 OSAddAtomic(1, &nfsstats.srvcache_nonidemdonehits);
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 }
270 } else if (rp->rc_flag & RC_REPMBUF) {
271 OSAddAtomic(1, &nfsstats.srvcache_nonidemdonehits);
272 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, mrepp);
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 }
279 } else {
280 OSAddAtomic(1, &nfsstats.srvcache_idemdonehits);
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;
287 wakeup(rp);
288 }
289 lck_mtx_unlock(nfsrv_reqcache_mutex);
290 return (ret);
291 }
292 }
293 OSAddAtomic(1, &nfsstats.srvcache_misses);
294 if (nfsrv_reqcache_count < nfsrv_reqcache_size) {
295 /* try to allocate a new entry */
296 MALLOC(rp, struct nfsrvcache *, sizeof *rp, M_NFSD, M_WAITOK);
297 if (rp) {
298 bzero((char *)rp, sizeof *rp);
299 nfsrv_reqcache_count++;
300 rp->rc_flag = RC_LOCKED;
301 }
302 } else {
303 rp = NULL;
304 }
305 if (!rp) {
306 /* try to reuse the least recently used entry */
307 rp = nfsrv_reqcache_lruhead.tqh_first;
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 }
314 while ((rp->rc_flag & RC_LOCKED) != 0) {
315 rp->rc_flag |= RC_WANTED;
316 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
317 rp = nfsrv_reqcache_lruhead.tqh_first;
318 }
319 rp->rc_flag |= RC_LOCKED;
320 LIST_REMOVE(rp, rc_hash);
321 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
322 if (rp->rc_flag & RC_REPMBUF)
323 mbuf_freem(rp->rc_reply);
324 if (rp->rc_flag & RC_NAM)
325 mbuf_freem(rp->rc_nam);
326 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
327 }
328 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
329 rp->rc_state = RC_INPROG;
330 rp->rc_xid = nd->nd_retxid;
331 saddr = mbuf_data(nd->nd_nam);
332 rp->rc_family = saddr->sa_family;
333 switch (saddr->sa_family) {
334 case AF_INET:
335 rp->rc_flag |= RC_INETADDR;
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;
341 break;
342 default:
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;
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;
355 wakeup(rp);
356 }
357 lck_mtx_unlock(nfsrv_reqcache_mutex);
358 return (RC_DOIT);
359 }
360
361 /*
362 * Update a request cache entry after the rpc has been done
363 */
364 void
365 nfsrv_updatecache(
366 struct nfsrv_descript *nd,
367 int repvalid,
368 mbuf_t repmbuf)
369 {
370 struct nfsrvcache *rp;
371 int error;
372
373 if (!nd->nd_nam2)
374 return;
375 lck_mtx_lock(nfsrv_reqcache_mutex);
376 loop:
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 &&
380 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
381 if ((rp->rc_flag & RC_LOCKED) != 0) {
382 rp->rc_flag |= RC_WANTED;
383 msleep(rp, nfsrv_reqcache_mutex, PZERO-1, "nfsrc", NULL);
384 goto loop;
385 }
386 rp->rc_flag |= RC_LOCKED;
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 }
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]) {
405 if ((nd->nd_vers == NFS_VER2) &&
406 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
407 rp->rc_status = nd->nd_repstat;
408 rp->rc_flag |= RC_REPSTATUS;
409 } else {
410 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
411 if (!error)
412 rp->rc_flag |= RC_REPMBUF;
413 }
414 }
415 rp->rc_flag &= ~RC_LOCKED;
416 if (rp->rc_flag & RC_WANTED) {
417 rp->rc_flag &= ~RC_WANTED;
418 wakeup(rp);
419 }
420 lck_mtx_unlock(nfsrv_reqcache_mutex);
421 return;
422 }
423 }
424 lck_mtx_unlock(nfsrv_reqcache_mutex);
425 }
426
427 /*
428 * Clean out the cache. Called when the last nfsd terminates.
429 */
430 void
431 nfsrv_cleancache(void)
432 {
433 struct nfsrvcache *rp, *nextrp;
434
435 lck_mtx_lock(nfsrv_reqcache_mutex);
436 for (rp = nfsrv_reqcache_lruhead.tqh_first; rp != 0; rp = nextrp) {
437 nextrp = rp->rc_lru.tqe_next;
438 LIST_REMOVE(rp, rc_hash);
439 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
440 _FREE(rp, M_NFSD);
441 }
442 nfsrv_reqcache_count = 0;
443 FREE(nfsrv_reqcache_hashtbl, M_TEMP);
444 lck_mtx_unlock(nfsrv_reqcache_mutex);
445 }
446
447 #endif /* NFSSERVER */