1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * %sccs.include.redist.c% 9 * 10 * @(#)nfs_srvcache.c 8.3 (Berkeley) 03/30/95 11 */ 12 13 /* 14 * Reference: Chet Juszczak, "Improving the Performance and Correctness 15 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 16 * pages 53-63. San Diego, February 1989. 17 */ 18 #include <sys/param.h> 19 #include <sys/vnode.h> 20 #include <sys/mount.h> 21 #include <sys/kernel.h> 22 #include <sys/systm.h> 23 #include <sys/proc.h> 24 #include <sys/mbuf.h> 25 #include <sys/malloc.h> 26 #include <sys/socket.h> 27 #include <sys/socketvar.h> 28 29 #include <netinet/in.h> 30 #ifdef ISO 31 #include <netiso/iso.h> 32 #endif 33 #include <nfs/nfsm_subs.h> 34 #include <nfs/rpcv2.h> 35 #include <nfs/nfsproto.h> 36 #include <nfs/nfs.h> 37 #include <nfs/nfsrvcache.h> 38 #include <nfs/nqnfs.h> 39 40 extern struct nfsstats nfsstats; 41 extern int nfsv2_procid[NFS_NPROCS]; 42 long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 43 44 #define NFSRCHASH(xid) \ 45 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) 46 LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 47 TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 48 u_long nfsrvhash; 49 50 #define TRUE 1 51 #define FALSE 0 52 53 #define NETFAMILY(rp) \ 54 (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 55 56 /* 57 * Static array that defines which nfs rpc's are nonidempotent 58 */ 59 int nonidempotent[NFS_NPROCS] = { 60 FALSE, 61 FALSE, 62 TRUE, 63 FALSE, 64 FALSE, 65 FALSE, 66 FALSE, 67 TRUE, 68 TRUE, 69 TRUE, 70 TRUE, 71 TRUE, 72 TRUE, 73 TRUE, 74 TRUE, 75 TRUE, 76 FALSE, 77 FALSE, 78 FALSE, 79 FALSE, 80 FALSE, 81 FALSE, 82 FALSE, 83 FALSE, 84 FALSE, 85 FALSE, 86 }; 87 88 /* True iff the rpc reply is an nfs status ONLY! */ 89 static int nfsv2_repstat[NFS_NPROCS] = { 90 FALSE, 91 FALSE, 92 FALSE, 93 FALSE, 94 FALSE, 95 FALSE, 96 FALSE, 97 FALSE, 98 FALSE, 99 FALSE, 100 TRUE, 101 TRUE, 102 TRUE, 103 TRUE, 104 FALSE, 105 TRUE, 106 FALSE, 107 FALSE, 108 }; 109 110 /* 111 * Initialize the server request cache list 112 */ 113 void 114 nfsrv_initcache() 115 { 116 117 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash); 118 TAILQ_INIT(&nfsrvlruhead); 119 } 120 121 /* 122 * Look for the request in the cache 123 * If found then 124 * return action and optionally reply 125 * else 126 * insert it in the cache 127 * 128 * The rules are as follows: 129 * - if in progress, return DROP request 130 * - if completed within DELAY of the current time, return DROP it 131 * - if completed a longer time ago return REPLY if the reply was cached or 132 * return DOIT 133 * Update/add new request at end of lru list 134 */ 135 int 136 nfsrv_getcache(nd, slp, repp) 137 register struct nfsrv_descript *nd; 138 struct nfssvc_sock *slp; 139 struct mbuf **repp; 140 { 141 register struct nfsrvcache *rp; 142 struct mbuf *mb; 143 struct sockaddr_in *saddr; 144 caddr_t bpos; 145 int ret; 146 147 /* 148 * Don't cache recent requests for reliable transport protocols. 149 * (Maybe we should for the case of a reconnect, but..) 150 */ 151 if (!nd->nd_nam2) 152 return (RC_DOIT); 153 loop: 154 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 155 rp = rp->rc_hash.le_next) { 156 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 157 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 158 if ((rp->rc_flag & RC_LOCKED) != 0) { 159 rp->rc_flag |= RC_WANTED; 160 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 161 goto loop; 162 } 163 rp->rc_flag |= RC_LOCKED; 164 /* If not at end of LRU chain, move it there */ 165 if (rp->rc_lru.tqe_next) { 166 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 167 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 168 } 169 if (rp->rc_state == RC_UNUSED) 170 panic("nfsrv cache"); 171 if (rp->rc_state == RC_INPROG) { 172 nfsstats.srvcache_inproghits++; 173 ret = RC_DROPIT; 174 } else if (rp->rc_flag & RC_REPSTATUS) { 175 nfsstats.srvcache_nonidemdonehits++; 176 nfs_rephead(0, nd, slp, rp->rc_status, 177 0, (u_quad_t *)0, repp, &mb, &bpos); 178 ret = RC_REPLY; 179 } else if (rp->rc_flag & RC_REPMBUF) { 180 nfsstats.srvcache_nonidemdonehits++; 181 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 182 M_WAIT); 183 ret = RC_REPLY; 184 } else { 185 nfsstats.srvcache_idemdonehits++; 186 rp->rc_state = RC_INPROG; 187 ret = RC_DOIT; 188 } 189 rp->rc_flag &= ~RC_LOCKED; 190 if (rp->rc_flag & RC_WANTED) { 191 rp->rc_flag &= ~RC_WANTED; 192 wakeup((caddr_t)rp); 193 } 194 return (ret); 195 } 196 } 197 nfsstats.srvcache_misses++; 198 if (numnfsrvcache < desirednfsrvcache) { 199 rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 200 M_NFSD, M_WAITOK); 201 bzero((char *)rp, sizeof *rp); 202 numnfsrvcache++; 203 rp->rc_flag = RC_LOCKED; 204 } else { 205 rp = nfsrvlruhead.tqh_first; 206 while ((rp->rc_flag & RC_LOCKED) != 0) { 207 rp->rc_flag |= RC_WANTED; 208 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 209 rp = nfsrvlruhead.tqh_first; 210 } 211 rp->rc_flag |= RC_LOCKED; 212 LIST_REMOVE(rp, rc_hash); 213 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 214 if (rp->rc_flag & RC_REPMBUF) 215 m_freem(rp->rc_reply); 216 if (rp->rc_flag & RC_NAM) 217 MFREE(rp->rc_nam, mb); 218 rp->rc_flag &= (RC_LOCKED | RC_WANTED); 219 } 220 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 221 rp->rc_state = RC_INPROG; 222 rp->rc_xid = nd->nd_retxid; 223 saddr = mtod(nd->nd_nam, struct sockaddr_in *); 224 switch (saddr->sin_family) { 225 case AF_INET: 226 rp->rc_flag |= RC_INETADDR; 227 rp->rc_inetaddr = saddr->sin_addr.s_addr; 228 break; 229 case AF_ISO: 230 default: 231 rp->rc_flag |= RC_NAM; 232 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); 233 break; 234 }; 235 rp->rc_proc = nd->nd_procnum; 236 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 237 rp->rc_flag &= ~RC_LOCKED; 238 if (rp->rc_flag & RC_WANTED) { 239 rp->rc_flag &= ~RC_WANTED; 240 wakeup((caddr_t)rp); 241 } 242 return (RC_DOIT); 243 } 244 245 /* 246 * Update a request cache entry after the rpc has been done 247 */ 248 void 249 nfsrv_updatecache(nd, repvalid, repmbuf) 250 register struct nfsrv_descript *nd; 251 int repvalid; 252 struct mbuf *repmbuf; 253 { 254 register struct nfsrvcache *rp; 255 256 if (!nd->nd_nam2) 257 return; 258 loop: 259 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 260 rp = rp->rc_hash.le_next) { 261 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 262 netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 263 if ((rp->rc_flag & RC_LOCKED) != 0) { 264 rp->rc_flag |= RC_WANTED; 265 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 266 goto loop; 267 } 268 rp->rc_flag |= RC_LOCKED; 269 rp->rc_state = RC_DONE; 270 /* 271 * If we have a valid reply update status and save 272 * the reply for non-idempotent rpc's. 273 */ 274 if (repvalid && nonidempotent[nd->nd_procnum]) { 275 if ((nd->nd_flag & ND_NFSV3) == 0 && 276 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 277 rp->rc_status = nd->nd_repstat; 278 rp->rc_flag |= RC_REPSTATUS; 279 } else { 280 rp->rc_reply = m_copym(repmbuf, 281 0, M_COPYALL, M_WAIT); 282 rp->rc_flag |= RC_REPMBUF; 283 } 284 } 285 rp->rc_flag &= ~RC_LOCKED; 286 if (rp->rc_flag & RC_WANTED) { 287 rp->rc_flag &= ~RC_WANTED; 288 wakeup((caddr_t)rp); 289 } 290 return; 291 } 292 } 293 } 294 295 /* 296 * Clean out the cache. Called when the last nfsd terminates. 297 */ 298 void 299 nfsrv_cleancache() 300 { 301 register struct nfsrvcache *rp, *nextrp; 302 303 for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) { 304 nextrp = rp->rc_lru.tqe_next; 305 LIST_REMOVE(rp, rc_hash); 306 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 307 free(rp, M_NFSD); 308 } 309 numnfsrvcache = 0; 310 } 311