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 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70
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/mbuf.h>
83 #include <sys/kpi_mbuf.h>
84 #include <sys/malloc.h>
85 #include <sys/socket.h>
86 #include <libkern/OSAtomic.h>
87
88 #include <netinet/in.h>
89 #include <nfs/rpcv2.h>
90 #include <nfs/nfsproto.h>
91 #include <nfs/nfs.h>
92 #include <nfs/nfsrvcache.h>
93
94 extern int nfsv2_procid[NFS_NPROCS];
95 static int nfsrv_reqcache_count;
96 int nfsrv_reqcache_size = NFSRVCACHESIZ;
97
98 #define NFSRCHASH(xid) \
99 (&nfsrv_reqcache_hashtbl[((xid) + ((xid) >> 24)) & nfsrv_reqcache_hash])
100 static LIST_HEAD(nfsrv_reqcache_hash, nfsrvcache) * nfsrv_reqcache_hashtbl;
101 static TAILQ_HEAD(nfsrv_reqcache_lru, nfsrvcache) nfsrv_reqcache_lruhead;
102 static u_long nfsrv_reqcache_hash;
103
104 static LCK_GRP_DECLARE(nfsrv_reqcache_lck_grp, "nfsrv_reqcache");
105 static LCK_MTX_DECLARE(nfsrv_reqcache_mutex, &nfsrv_reqcache_lck_grp);
106
107 /*
108 * Static array that defines which nfs rpc's are nonidempotent
109 */
110 static int nonidempotent[NFS_NPROCS] = {
111 FALSE,
112 FALSE,
113 TRUE,
114 FALSE,
115 FALSE,
116 FALSE,
117 FALSE,
118 TRUE,
119 TRUE,
120 TRUE,
121 TRUE,
122 TRUE,
123 TRUE,
124 TRUE,
125 TRUE,
126 TRUE,
127 FALSE,
128 FALSE,
129 FALSE,
130 FALSE,
131 FALSE,
132 FALSE,
133 FALSE,
134 };
135
136 /* True iff the rpc reply is an nfs status ONLY! */
137 static int nfsv2_repstat[NFS_NPROCS] = {
138 FALSE,
139 FALSE,
140 FALSE,
141 FALSE,
142 FALSE,
143 FALSE,
144 FALSE,
145 FALSE,
146 FALSE,
147 FALSE,
148 TRUE,
149 TRUE,
150 TRUE,
151 TRUE,
152 FALSE,
153 TRUE,
154 FALSE,
155 FALSE,
156 };
157
158 /*
159 * Initialize the server request cache list
160 */
161 void
nfsrv_initcache(void)162 nfsrv_initcache(void)
163 {
164 if (nfsrv_reqcache_size <= 0) {
165 return;
166 }
167
168 lck_mtx_lock(&nfsrv_reqcache_mutex);
169 /* init nfs server request cache hash table */
170 nfsrv_reqcache_hashtbl = hashinit(nfsrv_reqcache_size, M_NFSD, &nfsrv_reqcache_hash);
171 TAILQ_INIT(&nfsrv_reqcache_lruhead);
172 lck_mtx_unlock(&nfsrv_reqcache_mutex);
173 }
174
175 /*
176 * This function compares two net addresses by family and returns TRUE
177 * if they are the same host.
178 * If there is any doubt, return FALSE.
179 * The AF_INET family is handled as a special case so that address mbufs
180 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
181 * Ditto for AF_INET6 which is only 16 bytes.
182 */
183 static int
netaddr_match(int family,union nethostaddr * haddr,mbuf_t nam)184 netaddr_match(
185 int family,
186 union nethostaddr *haddr,
187 mbuf_t nam)
188 {
189 struct sockaddr_in *inetaddr;
190 struct sockaddr_in6 *inet6addr;
191
192 switch (family) {
193 case AF_INET:
194 inetaddr = SIN(mtod(nam, caddr_t));
195 if ((inetaddr->sin_family == AF_INET) &&
196 (inetaddr->sin_addr.s_addr == haddr->had_inetaddr)) {
197 return 1;
198 }
199 break;
200 case AF_INET6:
201 inet6addr = SIN6(mtod(nam, caddr_t));
202 if ((inet6addr->sin6_family == AF_INET6) &&
203 !bcmp(&inet6addr->sin6_addr, &haddr->had_inet6addr, sizeof(inet6addr->sin6_addr))) {
204 return 1;
205 }
206 break;
207 }
208 return 0;
209 }
210
211 /*
212 * Look for the request in the cache
213 * If found then
214 * return action and optionally reply
215 * else
216 * insert it in the cache
217 *
218 * The rules are as follows:
219 * - if in progress, return DROP request
220 * - if completed within DELAY of the current time, return DROP it
221 * - if completed a longer time ago return REPLY if the reply was cached or
222 * return DOIT
223 * Update/add new request at end of lru list
224 */
225 int
nfsrv_getcache(struct nfsrv_descript * nd,struct nfsrv_sock * slp,mbuf_t * mrepp)226 nfsrv_getcache(
227 struct nfsrv_descript *nd,
228 struct nfsrv_sock *slp,
229 mbuf_t *mrepp)
230 {
231 struct nfsrvcache *rp;
232 struct nfsm_chain nmrep;
233 struct sockaddr *saddr;
234 int ret, error;
235
236 /*
237 * Don't cache recent requests for reliable transport protocols.
238 * (Maybe we should for the case of a reconnect, but..)
239 */
240 if (!nd->nd_nam2) {
241 return RC_DOIT;
242 }
243 lck_mtx_lock(&nfsrv_reqcache_mutex);
244 loop:
245 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
246 rp = rp->rc_hash.le_next) {
247 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
248 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
249 if ((rp->rc_flag & RC_LOCKED) != 0) {
250 rp->rc_flag |= RC_WANTED;
251 msleep(rp, &nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
252 goto loop;
253 }
254 rp->rc_flag |= RC_LOCKED;
255 /* If not at end of LRU chain, move it there */
256 if (rp->rc_lru.tqe_next) {
257 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
258 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
259 }
260 if (rp->rc_state == RC_UNUSED) {
261 panic("nfsrv cache");
262 }
263 if (rp->rc_state == RC_INPROG) {
264 OSAddAtomic64(1, &nfsrvstats.srvcache_inproghits);
265 ret = RC_DROPIT;
266 } else if (rp->rc_flag & RC_REPSTATUS) {
267 OSAddAtomic64(1, &nfsrvstats.srvcache_nonidemdonehits);
268 nd->nd_repstat = rp->rc_status;
269 error = nfsrv_rephead(nd, slp, &nmrep, 0);
270 if (error) {
271 printf("nfsrv cache: reply alloc failed for nonidem request hit\n");
272 ret = RC_DROPIT;
273 *mrepp = NULL;
274 } else {
275 ret = RC_REPLY;
276 *mrepp = nmrep.nmc_mhead;
277 }
278 } else if (rp->rc_flag & RC_REPMBUF) {
279 OSAddAtomic64(1, &nfsrvstats.srvcache_nonidemdonehits);
280 error = mbuf_copym(rp->rc_reply, 0, MBUF_COPYALL, MBUF_WAITOK, mrepp);
281 if (error) {
282 printf("nfsrv cache: reply copym failed for nonidem request hit\n");
283 ret = RC_DROPIT;
284 } else {
285 ret = RC_REPLY;
286 }
287 } else {
288 OSAddAtomic64(1, &nfsrvstats.srvcache_idemdonehits);
289 rp->rc_state = RC_INPROG;
290 ret = RC_DOIT;
291 }
292 rp->rc_flag &= ~RC_LOCKED;
293 if (rp->rc_flag & RC_WANTED) {
294 rp->rc_flag &= ~RC_WANTED;
295 wakeup(rp);
296 }
297 lck_mtx_unlock(&nfsrv_reqcache_mutex);
298 return ret;
299 }
300 }
301 OSAddAtomic64(1, &nfsrvstats.srvcache_misses);
302 if (nfsrv_reqcache_count < nfsrv_reqcache_size) {
303 /* try to allocate a new entry */
304 rp = kalloc_type(struct nfsrvcache, Z_WAITOK | Z_ZERO | Z_NOFAIL);
305 rp->rc_flag = RC_LOCKED;
306 nfsrv_reqcache_count++;
307 } else {
308 rp = NULL;
309 }
310 if (!rp) {
311 /* try to reuse the least recently used entry */
312 rp = nfsrv_reqcache_lruhead.tqh_first;
313 if (!rp) {
314 /* no entry to reuse? */
315 /* OK, we just won't be able to cache this request */
316 lck_mtx_unlock(&nfsrv_reqcache_mutex);
317 return RC_DOIT;
318 }
319 while ((rp->rc_flag & RC_LOCKED) != 0) {
320 rp->rc_flag |= RC_WANTED;
321 msleep(rp, &nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
322 rp = nfsrv_reqcache_lruhead.tqh_first;
323 }
324 rp->rc_flag |= RC_LOCKED;
325 LIST_REMOVE(rp, rc_hash);
326 TAILQ_REMOVE(&nfsrv_reqcache_lruhead, rp, rc_lru);
327 if (rp->rc_flag & RC_REPMBUF) {
328 mbuf_freem(rp->rc_reply);
329 }
330 if (rp->rc_flag & RC_NAM) {
331 mbuf_freem(rp->rc_nam);
332 }
333 rp->rc_flag &= (RC_LOCKED | RC_WANTED);
334 }
335 TAILQ_INSERT_TAIL(&nfsrv_reqcache_lruhead, rp, rc_lru);
336 rp->rc_state = RC_INPROG;
337 rp->rc_xid = nd->nd_retxid;
338 saddr = SA(mtod(nd->nd_nam, caddr_t));
339 rp->rc_family = saddr->sa_family;
340 switch (saddr->sa_family) {
341 case AF_INET:
342 rp->rc_flag |= RC_INETADDR;
343 rp->rc_inetaddr = ((struct sockaddr_in*)saddr)->sin_addr.s_addr;
344 break;
345 case AF_INET6:
346 rp->rc_flag |= RC_INETADDR;
347 rp->rc_inet6addr = ((struct sockaddr_in6*)saddr)->sin6_addr;
348 break;
349 default:
350 error = mbuf_copym(nd->nd_nam, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_nam);
351 if (error) {
352 printf("nfsrv cache: nam copym failed\n");
353 } else {
354 rp->rc_flag |= RC_NAM;
355 }
356 break;
357 }
358 ;
359 rp->rc_proc = nd->nd_procnum;
360 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
361 rp->rc_flag &= ~RC_LOCKED;
362 if (rp->rc_flag & RC_WANTED) {
363 rp->rc_flag &= ~RC_WANTED;
364 wakeup(rp);
365 }
366 lck_mtx_unlock(&nfsrv_reqcache_mutex);
367 return RC_DOIT;
368 }
369
370 /*
371 * Update a request cache entry after the rpc has been done
372 */
373 void
nfsrv_updatecache(struct nfsrv_descript * nd,int repvalid,mbuf_t repmbuf)374 nfsrv_updatecache(
375 struct nfsrv_descript *nd,
376 int repvalid,
377 mbuf_t repmbuf)
378 {
379 struct nfsrvcache *rp;
380 int error;
381
382 if (!nd->nd_nam2) {
383 return;
384 }
385 lck_mtx_lock(&nfsrv_reqcache_mutex);
386 loop:
387 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
388 rp = rp->rc_hash.le_next) {
389 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
390 netaddr_match(rp->rc_family, &rp->rc_haddr, nd->nd_nam)) {
391 if ((rp->rc_flag & RC_LOCKED) != 0) {
392 rp->rc_flag |= RC_WANTED;
393 msleep(rp, &nfsrv_reqcache_mutex, PZERO - 1, "nfsrc", NULL);
394 goto loop;
395 }
396 rp->rc_flag |= RC_LOCKED;
397 if (rp->rc_state == RC_DONE) {
398 /*
399 * This can occur if the cache is too small.
400 * Retransmits of the same request aren't
401 * dropped so we may see the operation
402 * complete more then once.
403 */
404 if (rp->rc_flag & RC_REPMBUF) {
405 mbuf_freem(rp->rc_reply);
406 rp->rc_flag &= ~RC_REPMBUF;
407 }
408 }
409 rp->rc_state = RC_DONE;
410 /*
411 * If we have a valid reply update status and save
412 * the reply for non-idempotent rpc's.
413 */
414 if (repvalid && nonidempotent[nd->nd_procnum]) {
415 if ((nd->nd_vers == NFS_VER2) &&
416 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
417 rp->rc_status = nd->nd_repstat;
418 rp->rc_flag |= RC_REPSTATUS;
419 } else {
420 error = mbuf_copym(repmbuf, 0, MBUF_COPYALL, MBUF_WAITOK, &rp->rc_reply);
421 if (!error) {
422 rp->rc_flag |= RC_REPMBUF;
423 }
424 }
425 }
426 rp->rc_flag &= ~RC_LOCKED;
427 if (rp->rc_flag & RC_WANTED) {
428 rp->rc_flag &= ~RC_WANTED;
429 wakeup(rp);
430 }
431 lck_mtx_unlock(&nfsrv_reqcache_mutex);
432 return;
433 }
434 }
435 lck_mtx_unlock(&nfsrv_reqcache_mutex);
436 }
437
438 /*
439 * Clean out the cache. Called when the last nfsd terminates.
440 */
441 void
nfsrv_cleancache(void)442 nfsrv_cleancache(void)
443 {
444 struct nfsrvcache *rp, *nextrp;
445
446 lck_mtx_lock(&nfsrv_reqcache_mutex);
447 TAILQ_FOREACH_SAFE(rp, &nfsrv_reqcache_lruhead, rc_lru, nextrp) {
448 kfree_type(struct nfsrvcache, rp);
449 }
450 hashdestroy(nfsrv_reqcache_hashtbl, M_NFSD, nfsrv_reqcache_hash);
451 nfsrv_reqcache_hash = 0;
452 nfsrv_reqcache_count = 0;
453 TAILQ_INIT(&nfsrv_reqcache_lruhead);
454 lck_mtx_unlock(&nfsrv_reqcache_mutex);
455 }
456
457 #endif /* CONFIG_NFS_SERVER */
458