1 /* $OpenBSD: nfs_srvcache.c,v 1.23 2009/06/04 18:36:43 thib 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. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95 36 */ 37 38 /* 39 * Reference: Chet Juszczak, "Improving the Performance and Correctness 40 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 41 * pages 53-63. San Diego, February 1989. 42 */ 43 #include <sys/param.h> 44 #include <sys/mount.h> 45 #include <sys/kernel.h> 46 #include <sys/systm.h> 47 #include <sys/proc.h> 48 #include <sys/mbuf.h> 49 #include <sys/malloc.h> 50 #include <sys/socket.h> 51 #include <sys/queue.h> 52 53 #include <netinet/in.h> 54 #include <nfs/rpcv2.h> 55 #include <nfs/nfsproto.h> 56 #include <nfs/nfs.h> 57 #include <nfs/nfsrvcache.h> 58 #include <nfs/nfs_var.h> 59 60 extern struct nfsstats nfsstats; 61 extern int nfsv2_procid[NFS_NPROCS]; 62 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 63 64 struct nfsrvcache *nfsrv_lookupcache(struct nfsrv_descript *); 65 void nfsrv_cleanentry(struct nfsrvcache *); 66 67 #define NFSRCHASH(xid) \ 68 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) 69 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 70 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 71 u_long nfsrvhash; 72 73 #define NETFAMILY(rp) \ 74 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_UNSPEC) 75 76 /* Array that defines which nfs rpc's are nonidempotent */ 77 int nonidempotent[NFS_NPROCS] = { 78 0, 0, 1, 0, 0, 0, 0, 1, 79 1, 1, 1, 1, 1, 1, 1, 1, 80 0, 0, 0, 0, 0, 0, 0 81 }; 82 83 /* True iff the rpc reply is an nfs status ONLY! */ 84 int nfsv2_repstat[NFS_NPROCS] = { 85 0, 0, 0, 0, 0, 0, 0, 0, 86 0, 0, 1, 1, 1, 1, 0, 1, 87 0, 0 88 }; 89 90 void 91 nfsrv_cleanentry(struct nfsrvcache *rp) 92 { 93 if ((rp->rc_flag & RC_REPMBUF) != 0) 94 m_freem(rp->rc_reply); 95 96 if ((rp->rc_flag & RC_NAM) != 0) 97 m_free(rp->rc_nam); 98 99 rp->rc_flag &= ~(RC_REPSTATUS|RC_REPMBUF); 100 } 101 102 /* Initialize the server request cache list */ 103 void 104 nfsrv_initcache(void) 105 { 106 107 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash); 108 TAILQ_INIT(&nfsrvlruhead); 109 } 110 111 /* 112 * Look for the request in the cache 113 * If found then 114 * return action and optionally reply 115 * else 116 * insert it in the cache 117 * 118 * The rules are as follows: 119 * - if in progress, return DROP request 120 * - if completed within DELAY of the current time, return DROP it 121 * - if completed a longer time ago return REPLY if the reply was cached or 122 * return DOIT 123 * Update/add new request at end of lru list 124 */ 125 int 126 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, 127 struct mbuf **repp) 128 { 129 struct nfsrvcache *rp; 130 struct mbuf *mb; 131 struct sockaddr_in *saddr; 132 int ret; 133 134 /* 135 * Don't cache recent requests for reliable transport protocols. 136 * (Maybe we should for the case of a reconnect, but..) 137 */ 138 if (!nd->nd_nam2) 139 return (RC_DOIT); 140 141 rp = nfsrv_lookupcache(nd); 142 if (rp) { 143 /* If not at end of LRU chain, move it there */ 144 if (TAILQ_NEXT(rp, rc_lru)) { 145 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 146 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 147 } 148 if (rp->rc_state == RC_UNUSED) 149 panic("nfsrv cache"); 150 if (rp->rc_state == RC_INPROG) { 151 nfsstats.srvcache_inproghits++; 152 ret = RC_DROPIT; 153 } else if (rp->rc_flag & RC_REPSTATUS) { 154 nfsstats.srvcache_nonidemdonehits++; 155 nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb); 156 ret = RC_REPLY; 157 } else if (rp->rc_flag & RC_REPMBUF) { 158 nfsstats.srvcache_nonidemdonehits++; 159 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAIT); 160 ret = RC_REPLY; 161 } else { 162 nfsstats.srvcache_idemdonehits++; 163 rp->rc_state = RC_INPROG; 164 ret = RC_DOIT; 165 } 166 rp->rc_flag &= ~RC_LOCKED; 167 if (rp->rc_flag & RC_WANTED) { 168 rp->rc_flag &= ~RC_WANTED; 169 wakeup(rp); 170 } 171 return (ret); 172 } 173 174 nfsstats.srvcache_misses++; 175 if (numnfsrvcache < desirednfsrvcache) { 176 rp = malloc(sizeof(*rp), M_NFSD, M_WAITOK|M_ZERO); 177 numnfsrvcache++; 178 rp->rc_flag = RC_LOCKED; 179 } else { 180 rp = TAILQ_FIRST(&nfsrvlruhead); 181 while ((rp->rc_flag & RC_LOCKED) != 0) { 182 rp->rc_flag |= RC_WANTED; 183 tsleep(rp, PZERO-1, "nfsrc", 0); 184 rp = TAILQ_FIRST(&nfsrvlruhead); 185 } 186 rp->rc_flag |= RC_LOCKED; 187 LIST_REMOVE(rp, rc_hash); 188 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 189 nfsrv_cleanentry(rp); 190 rp->rc_flag &= (RC_LOCKED | RC_WANTED); 191 } 192 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 193 rp->rc_state = RC_INPROG; 194 rp->rc_xid = nd->nd_retxid; 195 saddr = mtod(nd->nd_nam, struct sockaddr_in *); 196 switch (saddr->sin_family) { 197 case AF_INET: 198 rp->rc_flag |= RC_INETADDR; 199 rp->rc_inetaddr = saddr->sin_addr.s_addr; 200 break; 201 default: 202 rp->rc_flag |= RC_NAM; 203 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); 204 break; 205 }; 206 rp->rc_proc = nd->nd_procnum; 207 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 208 rp->rc_flag &= ~RC_LOCKED; 209 if (rp->rc_flag & RC_WANTED) { 210 rp->rc_flag &= ~RC_WANTED; 211 wakeup(rp); 212 } 213 return (RC_DOIT); 214 } 215 216 /* Update a request cache entry after the rpc has been done */ 217 void 218 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, 219 struct mbuf *repmbuf) 220 { 221 struct nfsrvcache *rp; 222 223 if (!nd->nd_nam2) 224 return; 225 226 rp = nfsrv_lookupcache(nd); 227 if (rp) { 228 nfsrv_cleanentry(rp); 229 rp->rc_state = RC_DONE; 230 /* 231 * If we have a valid reply update status and save 232 * the reply for non-idempotent rpc's. 233 */ 234 if (repvalid && nonidempotent[nd->nd_procnum]) { 235 if ((nd->nd_flag & ND_NFSV3) == 0 && 236 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 237 rp->rc_status = nd->nd_repstat; 238 rp->rc_flag |= RC_REPSTATUS; 239 } else { 240 rp->rc_reply = m_copym(repmbuf, 0, M_COPYALL, 241 M_WAIT); 242 rp->rc_flag |= RC_REPMBUF; 243 } 244 } 245 rp->rc_flag &= ~RC_LOCKED; 246 if (rp->rc_flag & RC_WANTED) { 247 rp->rc_flag &= ~RC_WANTED; 248 wakeup(rp); 249 } 250 return; 251 } 252 } 253 254 /* Clean out the cache. Called when the last nfsd terminates. */ 255 void 256 nfsrv_cleancache(void) 257 { 258 struct nfsrvcache *rp, *nextrp; 259 260 for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) { 261 nextrp = TAILQ_NEXT(rp, rc_lru); 262 LIST_REMOVE(rp, rc_hash); 263 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 264 nfsrv_cleanentry(rp); 265 free(rp, M_NFSD); 266 } 267 numnfsrvcache = 0; 268 } 269 270 struct nfsrvcache * 271 nfsrv_lookupcache(struct nfsrv_descript *nd) 272 { 273 struct nfsrvcache *rp; 274 275 loop: 276 LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) { 277 if (nd->nd_retxid == rp->rc_xid && 278 nd->nd_procnum == rp->rc_proc && 279 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 280 if ((rp->rc_flag & RC_LOCKED)) { 281 rp->rc_flag |= RC_WANTED; 282 tsleep(rp, PZERO - 1, "nfsrc", 0); 283 goto loop; 284 } 285 rp->rc_flag |= RC_LOCKED; 286 return (rp); 287 } 288 } 289 290 return (NULL); 291 } 292