2  * Copyright (c) 2000-2009 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 
   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. 
  15  * Please obtain a copy of the License at 
  16  * http://www.opensource.apple.com/apsl/ and read it before using this file. 
  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. 
  26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 
  28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 
  30  * Copyright (c) 1989, 1993 
  31  *      The Regents of the University of California.  All rights reserved. 
  33  * This code is derived from software contributed to Berkeley by 
  34  * Rick Macklem at The University of Guelph. 
  36  * Redistribution and use in source and binary forms, with or without 
  37  * modification, are permitted provided that the following conditions 
  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. 
  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 
  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 $ 
  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. 
  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> 
  80 #include <sys/kpi_mbuf.h> 
  81 #include <sys/malloc.h> 
  82 #include <sys/socket.h> 
  83 #include <libkern/OSAtomic.h> 
  85 #include <netinet/in.h> 
  86 #include <nfs/rpcv2.h> 
  87 #include <nfs/nfsproto.h> 
  89 #include <nfs/nfsrvcache.h> 
  91 extern int nfsv2_procid
[NFS_NPROCS
]; 
  92 static int nfsrv_reqcache_count
; 
  93 int nfsrv_reqcache_size 
= NFSRVCACHESIZ
; 
  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
; 
 101 lck_grp_t 
*nfsrv_reqcache_lck_grp
; 
 102 lck_mtx_t 
*nfsrv_reqcache_mutex
; 
 105  * Static array that defines which nfs rpc's are nonidempotent 
 107 static int nonidempotent
[NFS_NPROCS
] = { 
 133 /* True iff the rpc reply is an nfs status ONLY! */ 
 134 static int nfsv2_repstat
[NFS_NPROCS
] = { 
 156  * Initialize the server request cache list 
 159 nfsrv_initcache(void) 
 161         if (nfsrv_reqcache_size 
<= 0) 
 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
); 
 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. 
 181         union nethostaddr 
*haddr
, 
 184         struct sockaddr_in 
*inetaddr
; 
 188                 inetaddr 
= mbuf_data(nam
); 
 189                 if (inetaddr
->sin_family 
== AF_INET 
&& 
 190                     inetaddr
->sin_addr
.s_addr 
== haddr
->had_inetaddr
) 
 200  * Look for the request in the cache 
 202  *    return action and optionally reply 
 204  *    insert it in the cache 
 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 
 211  * Update/add new request at end of lru list 
 215         struct nfsrv_descript 
*nd
, 
 216         struct nfsrv_sock 
*slp
, 
 219         struct nfsrvcache 
*rp
; 
 220         struct nfsm_chain nmrep
; 
 221         struct sockaddr_in 
*saddr
; 
 225          * Don't cache recent requests for reliable transport protocols. 
 226          * (Maybe we should for the case of a reconnect, but..) 
 230         lck_mtx_lock(nfsrv_reqcache_mutex
); 
 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
); 
 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
); 
 247                         if (rp
->rc_state 
== RC_UNUSED
) 
 248                                 panic("nfsrv cache"); 
 249                         if (rp
->rc_state 
== RC_INPROG
) { 
 250                                 OSAddAtomic(1, &nfsstats
.srvcache_inproghits
); 
 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); 
 257                                         printf("nfsrv cache: reply alloc failed for nonidem request hit\n"); 
 262                                         *mrepp 
= nmrep
.nmc_mhead
; 
 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
); 
 268                                         printf("nfsrv cache: reply copym failed for nonidem request hit\n"); 
 274                                 OSAddAtomic(1, &nfsstats
.srvcache_idemdonehits
); 
 275                                 rp
->rc_state 
= RC_INPROG
; 
 278                         rp
->rc_flag 
&= ~RC_LOCKED
; 
 279                         if (rp
->rc_flag 
& RC_WANTED
) { 
 280                                 rp
->rc_flag 
&= ~RC_WANTED
; 
 283                         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 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
); 
 292                         bzero((char *)rp
, sizeof *rp
); 
 293                         nfsrv_reqcache_count
++; 
 294                         rp
->rc_flag 
= RC_LOCKED
; 
 300                 /* try to reuse the least recently used entry */ 
 301                 rp 
= nfsrv_reqcache_lruhead
.tqh_first
; 
 303                         /* no entry to reuse? */ 
 304                         /* OK, we just won't be able to cache this request */ 
 305                         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 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
; 
 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
); 
 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
) { 
 328                 rp
->rc_flag 
|= RC_INETADDR
; 
 329                 rp
->rc_inetaddr 
= saddr
->sin_addr
.s_addr
; 
 332                 error 
= mbuf_copym(nd
->nd_nam
, 0, MBUF_COPYALL
, MBUF_WAITOK
, &rp
->rc_nam
); 
 334                         printf("nfsrv cache: nam copym failed\n"); 
 336                         rp
->rc_flag 
|= RC_NAM
; 
 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
; 
 346         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 351  * Update a request cache entry after the rpc has been done 
 355         struct nfsrv_descript 
*nd
, 
 359         struct nfsrvcache 
*rp
; 
 364         lck_mtx_lock(nfsrv_reqcache_mutex
); 
 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
); 
 375                         rp
->rc_flag 
|= RC_LOCKED
; 
 376                         if (rp
->rc_state 
== RC_DONE
) { 
 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. 
 383                                 if (rp
->rc_flag 
& RC_REPMBUF
) { 
 384                                         mbuf_freem(rp
->rc_reply
); 
 385                                         rp
->rc_flag 
&= ~RC_REPMBUF
; 
 388                         rp
->rc_state 
= RC_DONE
; 
 390                          * If we have a valid reply update status and save 
 391                          * the reply for non-idempotent rpc's. 
 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
; 
 399                                         error 
= mbuf_copym(repmbuf
, 0, MBUF_COPYALL
, MBUF_WAITOK
, &rp
->rc_reply
); 
 401                                                 rp
->rc_flag 
|= RC_REPMBUF
; 
 404                         rp
->rc_flag 
&= ~RC_LOCKED
; 
 405                         if (rp
->rc_flag 
& RC_WANTED
) { 
 406                                 rp
->rc_flag 
&= ~RC_WANTED
; 
 409                         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 413         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 417  * Clean out the cache. Called when the last nfsd terminates. 
 420 nfsrv_cleancache(void) 
 422         struct nfsrvcache 
*rp
, *nextrp
; 
 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
); 
 431         nfsrv_reqcache_count 
= 0; 
 432         FREE(nfsrv_reqcache_hashtbl
, M_TEMP
); 
 433         lck_mtx_unlock(nfsrv_reqcache_mutex
); 
 436 #endif /* NFSSERVER */