1 /* $OpenBSD: nfs_srvcache.c,v 1.6 1999/04/28 09:28:17 art Exp $ */ 2 /* $NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Rick Macklem at The University of Guelph. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95 40 */ 41 42 /* 43 * Reference: Chet Juszczak, "Improving the Performance and Correctness 44 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 45 * pages 53-63. San Diego, February 1989. 46 */ 47 #include <sys/param.h> 48 #include <sys/vnode.h> 49 #include <sys/mount.h> 50 #include <sys/kernel.h> 51 #include <sys/systm.h> 52 #include <sys/proc.h> 53 #include <sys/mbuf.h> 54 #include <sys/malloc.h> 55 #include <sys/socket.h> 56 #include <sys/socketvar.h> 57 58 #include <netinet/in.h> 59 #ifdef ISO 60 #include <netiso/iso.h> 61 #endif 62 #include <nfs/nfsm_subs.h> 63 #include <nfs/rpcv2.h> 64 #include <nfs/nfsproto.h> 65 #include <nfs/nfs.h> 66 #include <nfs/nfsrvcache.h> 67 #include <nfs/nqnfs.h> 68 #include <nfs/nfs_var.h> 69 70 extern struct nfsstats nfsstats; 71 extern int nfsv2_procid[NFS_NPROCS]; 72 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 73 74 #define NFSRCHASH(xid) \ 75 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) 76 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 77 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 78 u_long nfsrvhash; 79 80 #define TRUE 1 81 #define FALSE 0 82 83 #define NETFAMILY(rp) \ 84 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 85 86 /* 87 * Static array that defines which nfs rpc's are nonidempotent 88 */ 89 int nonidempotent[NFS_NPROCS] = { 90 FALSE, 91 FALSE, 92 TRUE, 93 FALSE, 94 FALSE, 95 FALSE, 96 FALSE, 97 TRUE, 98 TRUE, 99 TRUE, 100 TRUE, 101 TRUE, 102 TRUE, 103 TRUE, 104 TRUE, 105 TRUE, 106 FALSE, 107 FALSE, 108 FALSE, 109 FALSE, 110 FALSE, 111 FALSE, 112 FALSE, 113 FALSE, 114 FALSE, 115 FALSE, 116 }; 117 118 /* True iff the rpc reply is an nfs status ONLY! */ 119 static int nfsv2_repstat[NFS_NPROCS] = { 120 FALSE, 121 FALSE, 122 FALSE, 123 FALSE, 124 FALSE, 125 FALSE, 126 FALSE, 127 FALSE, 128 FALSE, 129 FALSE, 130 TRUE, 131 TRUE, 132 TRUE, 133 TRUE, 134 FALSE, 135 TRUE, 136 FALSE, 137 FALSE, 138 }; 139 140 /* 141 * Initialize the server request cache list 142 */ 143 void 144 nfsrv_initcache() 145 { 146 147 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash); 148 TAILQ_INIT(&nfsrvlruhead); 149 } 150 151 /* 152 * Look for the request in the cache 153 * If found then 154 * return action and optionally reply 155 * else 156 * insert it in the cache 157 * 158 * The rules are as follows: 159 * - if in progress, return DROP request 160 * - if completed within DELAY of the current time, return DROP it 161 * - if completed a longer time ago return REPLY if the reply was cached or 162 * return DOIT 163 * Update/add new request at end of lru list 164 */ 165 int 166 nfsrv_getcache(nd, slp, repp) 167 register struct nfsrv_descript *nd; 168 struct nfssvc_sock *slp; 169 struct mbuf **repp; 170 { 171 register struct nfsrvcache *rp; 172 struct mbuf *mb; 173 struct sockaddr_in *saddr; 174 caddr_t bpos; 175 int ret; 176 177 /* 178 * Don't cache recent requests for reliable transport protocols. 179 * (Maybe we should for the case of a reconnect, but..) 180 */ 181 if (!nd->nd_nam2) 182 return (RC_DOIT); 183 loop: 184 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 185 rp = rp->rc_hash.le_next) { 186 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 187 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 188 if ((rp->rc_flag & RC_LOCKED) != 0) { 189 rp->rc_flag |= RC_WANTED; 190 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 191 goto loop; 192 } 193 rp->rc_flag |= RC_LOCKED; 194 /* If not at end of LRU chain, move it there */ 195 if (rp->rc_lru.tqe_next) { 196 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 197 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 198 } 199 if (rp->rc_state == RC_UNUSED) 200 panic("nfsrv cache"); 201 if (rp->rc_state == RC_INPROG) { 202 nfsstats.srvcache_inproghits++; 203 ret = RC_DROPIT; 204 } else if (rp->rc_flag & RC_REPSTATUS) { 205 nfsstats.srvcache_nonidemdonehits++; 206 nfs_rephead(0, nd, slp, rp->rc_status, 207 0, (u_quad_t *)0, repp, &mb, &bpos); 208 ret = RC_REPLY; 209 } else if (rp->rc_flag & RC_REPMBUF) { 210 nfsstats.srvcache_nonidemdonehits++; 211 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 212 M_WAIT); 213 ret = RC_REPLY; 214 } else { 215 nfsstats.srvcache_idemdonehits++; 216 rp->rc_state = RC_INPROG; 217 ret = RC_DOIT; 218 } 219 rp->rc_flag &= ~RC_LOCKED; 220 if (rp->rc_flag & RC_WANTED) { 221 rp->rc_flag &= ~RC_WANTED; 222 wakeup((caddr_t)rp); 223 } 224 return (ret); 225 } 226 } 227 nfsstats.srvcache_misses++; 228 if (numnfsrvcache < desirednfsrvcache) { 229 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 230 M_NFSD, M_WAITOK); 231 bzero((char *)rp, sizeof *rp); 232 numnfsrvcache++; 233 rp->rc_flag = RC_LOCKED; 234 } else { 235 rp = nfsrvlruhead.tqh_first; 236 while ((rp->rc_flag & RC_LOCKED) != 0) { 237 rp->rc_flag |= RC_WANTED; 238 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 239 rp = nfsrvlruhead.tqh_first; 240 } 241 rp->rc_flag |= RC_LOCKED; 242 LIST_REMOVE(rp, rc_hash); 243 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 244 if (rp->rc_flag & RC_REPMBUF) 245 m_freem(rp->rc_reply); 246 if (rp->rc_flag & RC_NAM) 247 MFREE(rp->rc_nam, mb); 248 rp->rc_flag &= (RC_LOCKED | RC_WANTED); 249 } 250 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 251 rp->rc_state = RC_INPROG; 252 rp->rc_xid = nd->nd_retxid; 253 saddr = mtod(nd->nd_nam, struct sockaddr_in *); 254 switch (saddr->sin_family) { 255 case AF_INET: 256 rp->rc_flag |= RC_INETADDR; 257 rp->rc_inetaddr = saddr->sin_addr.s_addr; 258 break; 259 case AF_ISO: 260 default: 261 rp->rc_flag |= RC_NAM; 262 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); 263 break; 264 }; 265 rp->rc_proc = nd->nd_procnum; 266 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 267 rp->rc_flag &= ~RC_LOCKED; 268 if (rp->rc_flag & RC_WANTED) { 269 rp->rc_flag &= ~RC_WANTED; 270 wakeup((caddr_t)rp); 271 } 272 return (RC_DOIT); 273 } 274 275 /* 276 * Update a request cache entry after the rpc has been done 277 */ 278 void 279 nfsrv_updatecache(nd, repvalid, repmbuf) 280 register struct nfsrv_descript *nd; 281 int repvalid; 282 struct mbuf *repmbuf; 283 { 284 register struct nfsrvcache *rp; 285 286 if (!nd->nd_nam2) 287 return; 288 loop: 289 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 290 rp = rp->rc_hash.le_next) { 291 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 292 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 293 if ((rp->rc_flag & RC_LOCKED) != 0) { 294 rp->rc_flag |= RC_WANTED; 295 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 296 goto loop; 297 } 298 rp->rc_flag |= RC_LOCKED; 299 rp->rc_state = RC_DONE; 300 /* 301 * If we have a valid reply update status and save 302 * the reply for non-idempotent rpc's. 303 */ 304 if (repvalid && nonidempotent[nd->nd_procnum]) { 305 if ((nd->nd_flag & ND_NFSV3) == 0 && 306 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 307 rp->rc_status = nd->nd_repstat; 308 rp->rc_flag |= RC_REPSTATUS; 309 } else { 310 rp->rc_reply = m_copym(repmbuf, 311 0, M_COPYALL, M_WAIT); 312 rp->rc_flag |= RC_REPMBUF; 313 } 314 } 315 rp->rc_flag &= ~RC_LOCKED; 316 if (rp->rc_flag & RC_WANTED) { 317 rp->rc_flag &= ~RC_WANTED; 318 wakeup((caddr_t)rp); 319 } 320 return; 321 } 322 } 323 } 324 325 /* 326 * Clean out the cache. Called when the last nfsd terminates. 327 */ 328 void 329 nfsrv_cleancache() 330 { 331 register struct nfsrvcache *rp, *nextrp; 332 333 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) { 334 nextrp = rp->rc_lru.tqe_next; 335 LIST_REMOVE(rp, rc_hash); 336 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 337 free(rp, M_NFSD); 338 } 339 numnfsrvcache = 0; 340 } 341