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 91extern int nfsv2_procid[NFS_NPROCS]; 92static int nfsrv_reqcache_count; 93int nfsrv_reqcache_size = NFSRVCACHESIZ; 94 95#define NFSRCHASH(xid) \ 96 (&nfsrv_reqcache_hashtbl[((xid) + ((xid) >> 24)) & nfsrv_reqcache_hash]) 97LIST_HEAD(nfsrv_reqcache_hash, nfsrvcache) *nfsrv_reqcache_hashtbl; 98TAILQ_HEAD(nfsrv_reqcache_lru, nfsrvcache) nfsrv_reqcache_lruhead; 99u_long nfsrv_reqcache_hash; 100 101lck_grp_t *nfsrv_reqcache_lck_grp; 102lck_mtx_t *nfsrv_reqcache_mutex; 103 104/* 105 * Static array that defines which nfs rpc's are nonidempotent 106 */ 107static 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! */ 134static 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 */ 158void 159nfsrv_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 */ 179static int 180netaddr_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 */ 219int 220nfsrv_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); 237loop: 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 OSAddAtomic64(1, &nfsstats.srvcache_inproghits); 257 ret = RC_DROPIT; 258 } else if (rp->rc_flag & RC_REPSTATUS) { 259 OSAddAtomic64(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 OSAddAtomic64(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 OSAddAtomic64(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 OSAddAtomic64(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 */ 364void 365nfsrv_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); 376loop: 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 */ 430void 431nfsrv_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 */ 448