1*8d80561bSjsg /* $OpenBSD: nfs_srvcache.c,v 1.32 2024/09/18 05:21:19 jsg Exp $ */ 278530d46Smickey /* $NetBSD: nfs_srvcache.c,v 1.12 1996/02/18 11:53:49 fvdl Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1989, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * This code is derived from software contributed to Berkeley by 9df930be7Sderaadt * Rick Macklem at The University of Guelph. 10df930be7Sderaadt * 11df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 12df930be7Sderaadt * modification, are permitted provided that the following conditions 13df930be7Sderaadt * are met: 14df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 15df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 16df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 17df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 18df930be7Sderaadt * documentation and/or other materials provided with the distribution. 1929295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 20df930be7Sderaadt * may be used to endorse or promote products derived from this software 21df930be7Sderaadt * without specific prior written permission. 22df930be7Sderaadt * 23df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33df930be7Sderaadt * SUCH DAMAGE. 34df930be7Sderaadt * 3578530d46Smickey * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95 36df930be7Sderaadt */ 37df930be7Sderaadt 38df930be7Sderaadt /* 39df930be7Sderaadt * Reference: Chet Juszczak, "Improving the Performance and Correctness 40df930be7Sderaadt * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 41df930be7Sderaadt * pages 53-63. San Diego, February 1989. 42df930be7Sderaadt */ 43df930be7Sderaadt #include <sys/param.h> 44df930be7Sderaadt #include <sys/mount.h> 45df930be7Sderaadt #include <sys/systm.h> 46df930be7Sderaadt #include <sys/mbuf.h> 47df930be7Sderaadt #include <sys/malloc.h> 48df930be7Sderaadt #include <sys/socket.h> 49d8c62b08Sblambert #include <sys/queue.h> 50df930be7Sderaadt 519dc9bb81Sdlg #include <crypto/siphash.h> 529dc9bb81Sdlg 53df930be7Sderaadt #include <netinet/in.h> 5478530d46Smickey #include <nfs/nfsproto.h> 55df930be7Sderaadt #include <nfs/nfs.h> 56df930be7Sderaadt #include <nfs/nfsrvcache.h> 57370decb4Sniklas #include <nfs/nfs_var.h> 58df930be7Sderaadt 5978530d46Smickey extern struct nfsstats nfsstats; 6045db009aSmiod extern const int nfsv2_procid[NFS_NPROCS]; 61df930be7Sderaadt long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 62df930be7Sderaadt 63b2458ef5Sthib struct nfsrvcache *nfsrv_lookupcache(struct nfsrv_descript *); 646d8dfc9aSthib void nfsrv_cleanentry(struct nfsrvcache *); 65b2458ef5Sthib 66df930be7Sderaadt LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 679dc9bb81Sdlg SIPHASH_KEY nfsrvhashkey; 68df930be7Sderaadt TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 69df930be7Sderaadt u_long nfsrvhash; 709dc9bb81Sdlg #define NFSRCHASH(xid) \ 719dc9bb81Sdlg (&nfsrvhashtbl[SipHash24(&nfsrvhashkey, &(xid), sizeof(xid)) & nfsrvhash]) 72df930be7Sderaadt 73df930be7Sderaadt #define NETFAMILY(rp) \ 74bfbc2ff7Shenning (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_UNSPEC) 75df930be7Sderaadt 769a61f037Sthib /* Array that defines which nfs rpc's are nonidempotent */ 7745db009aSmiod static const int nonidempotent[NFS_NPROCS] = { 78a537d742Sthib 0, 0, 1, 0, 0, 0, 0, 1, 79a537d742Sthib 1, 1, 1, 1, 1, 1, 1, 1, 800a877889Sthib 0, 0, 0, 0, 0, 0, 0 81df930be7Sderaadt }; 82df930be7Sderaadt 83df930be7Sderaadt /* True iff the rpc reply is an nfs status ONLY! */ 8445db009aSmiod static const int nfsv2_repstat[NFS_NPROCS] = { 85a537d742Sthib 0, 0, 0, 0, 0, 0, 0, 0, 86a537d742Sthib 0, 0, 1, 1, 1, 1, 0, 1, 87a537d742Sthib 0, 0 88df930be7Sderaadt }; 89df930be7Sderaadt 906d8dfc9aSthib void 916d8dfc9aSthib nfsrv_cleanentry(struct nfsrvcache *rp) 926d8dfc9aSthib { 936d8dfc9aSthib if ((rp->rc_flag & RC_REPMBUF) != 0) 946d8dfc9aSthib m_freem(rp->rc_reply); 956d8dfc9aSthib 966d8dfc9aSthib if ((rp->rc_flag & RC_NAM) != 0) 976d8dfc9aSthib m_free(rp->rc_nam); 986d8dfc9aSthib 996d8dfc9aSthib rp->rc_flag &= ~(RC_REPSTATUS|RC_REPMBUF); 1006d8dfc9aSthib } 1016d8dfc9aSthib 1029a61f037Sthib /* Initialize the server request cache list */ 103370decb4Sniklas void 1049a61f037Sthib nfsrv_initcache(void) 105df930be7Sderaadt { 106df930be7Sderaadt 107a197ef32Sart nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, M_WAITOK, &nfsrvhash); 1089dc9bb81Sdlg arc4random_buf(&nfsrvhashkey, sizeof(nfsrvhashkey)); 109df930be7Sderaadt TAILQ_INIT(&nfsrvlruhead); 110df930be7Sderaadt } 111df930be7Sderaadt 112df930be7Sderaadt /* 113df930be7Sderaadt * Look for the request in the cache 114df930be7Sderaadt * If found then 115df930be7Sderaadt * return action and optionally reply 116df930be7Sderaadt * else 117df930be7Sderaadt * insert it in the cache 118df930be7Sderaadt * 119df930be7Sderaadt * The rules are as follows: 120df930be7Sderaadt * - if in progress, return DROP request 121df930be7Sderaadt * - if completed within DELAY of the current time, return DROP it 122df930be7Sderaadt * - if completed a longer time ago return REPLY if the reply was cached or 123df930be7Sderaadt * return DOIT 124df930be7Sderaadt * Update/add new request at end of lru list 125df930be7Sderaadt */ 126370decb4Sniklas int 1279a61f037Sthib nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, 1289a61f037Sthib struct mbuf **repp) 129df930be7Sderaadt { 1309dc9bb81Sdlg struct nfsrvhash *hash; 13104b702e4Sericj struct nfsrvcache *rp; 132df930be7Sderaadt struct mbuf *mb; 133df930be7Sderaadt struct sockaddr_in *saddr; 134df930be7Sderaadt int ret; 135df930be7Sderaadt 13678530d46Smickey /* 13778530d46Smickey * Don't cache recent requests for reliable transport protocols. 13878530d46Smickey * (Maybe we should for the case of a reconnect, but..) 13978530d46Smickey */ 14078530d46Smickey if (!nd->nd_nam2) 141df930be7Sderaadt return (RC_DOIT); 142b2458ef5Sthib 143b2458ef5Sthib rp = nfsrv_lookupcache(nd); 144b2458ef5Sthib if (rp) { 145df930be7Sderaadt /* If not at end of LRU chain, move it there */ 14604b702e4Sericj if (TAILQ_NEXT(rp, rc_lru)) { 147df930be7Sderaadt TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 148df930be7Sderaadt TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 149df930be7Sderaadt } 150df930be7Sderaadt if (rp->rc_state == RC_UNUSED) 151df930be7Sderaadt panic("nfsrv cache"); 152df930be7Sderaadt if (rp->rc_state == RC_INPROG) { 153df930be7Sderaadt nfsstats.srvcache_inproghits++; 154df930be7Sderaadt ret = RC_DROPIT; 155df930be7Sderaadt } else if (rp->rc_flag & RC_REPSTATUS) { 156df930be7Sderaadt nfsstats.srvcache_nonidemdonehits++; 15765faa1c0Sblambert nfs_rephead(0, nd, slp, rp->rc_status, repp, &mb); 158df930be7Sderaadt ret = RC_REPLY; 159df930be7Sderaadt } else if (rp->rc_flag & RC_REPMBUF) { 160df930be7Sderaadt nfsstats.srvcache_nonidemdonehits++; 161b2458ef5Sthib *repp = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAIT); 162df930be7Sderaadt ret = RC_REPLY; 163df930be7Sderaadt } else { 164df930be7Sderaadt nfsstats.srvcache_idemdonehits++; 165df930be7Sderaadt rp->rc_state = RC_INPROG; 166df930be7Sderaadt ret = RC_DOIT; 167df930be7Sderaadt } 168df930be7Sderaadt rp->rc_flag &= ~RC_LOCKED; 169df930be7Sderaadt if (rp->rc_flag & RC_WANTED) { 170df930be7Sderaadt rp->rc_flag &= ~RC_WANTED; 1719a61f037Sthib wakeup(rp); 172df930be7Sderaadt } 173df930be7Sderaadt return (ret); 174df930be7Sderaadt } 175b2458ef5Sthib 176df930be7Sderaadt nfsstats.srvcache_misses++; 177df930be7Sderaadt if (numnfsrvcache < desirednfsrvcache) { 1783e258649Sthib rp = malloc(sizeof(*rp), M_NFSD, M_WAITOK|M_ZERO); 179df930be7Sderaadt numnfsrvcache++; 180df930be7Sderaadt rp->rc_flag = RC_LOCKED; 181df930be7Sderaadt } else { 18204b702e4Sericj rp = TAILQ_FIRST(&nfsrvlruhead); 183df930be7Sderaadt while ((rp->rc_flag & RC_LOCKED) != 0) { 184df930be7Sderaadt rp->rc_flag |= RC_WANTED; 185ae991a59Smpi tsleep_nsec(rp, PZERO-1, "nfsrc", INFSLP); 18604b702e4Sericj rp = TAILQ_FIRST(&nfsrvlruhead); 187df930be7Sderaadt } 188df930be7Sderaadt rp->rc_flag |= RC_LOCKED; 189df930be7Sderaadt LIST_REMOVE(rp, rc_hash); 190df930be7Sderaadt TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 1916d8dfc9aSthib nfsrv_cleanentry(rp); 192df930be7Sderaadt rp->rc_flag &= (RC_LOCKED | RC_WANTED); 193df930be7Sderaadt } 194df930be7Sderaadt TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 195df930be7Sderaadt rp->rc_state = RC_INPROG; 196df930be7Sderaadt rp->rc_xid = nd->nd_retxid; 19778530d46Smickey saddr = mtod(nd->nd_nam, struct sockaddr_in *); 198df930be7Sderaadt switch (saddr->sin_family) { 199df930be7Sderaadt case AF_INET: 200df930be7Sderaadt rp->rc_flag |= RC_INETADDR; 201df930be7Sderaadt rp->rc_inetaddr = saddr->sin_addr.s_addr; 202df930be7Sderaadt break; 203df930be7Sderaadt default: 204df930be7Sderaadt rp->rc_flag |= RC_NAM; 20578530d46Smickey rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); 206df930be7Sderaadt break; 207*8d80561bSjsg } 208df930be7Sderaadt rp->rc_proc = nd->nd_procnum; 2099dc9bb81Sdlg hash = NFSRCHASH(nd->nd_retxid); 2109dc9bb81Sdlg LIST_INSERT_HEAD(hash, rp, rc_hash); 211df930be7Sderaadt rp->rc_flag &= ~RC_LOCKED; 212df930be7Sderaadt if (rp->rc_flag & RC_WANTED) { 213df930be7Sderaadt rp->rc_flag &= ~RC_WANTED; 2149a61f037Sthib wakeup(rp); 215df930be7Sderaadt } 216df930be7Sderaadt return (RC_DOIT); 217df930be7Sderaadt } 218df930be7Sderaadt 2199a61f037Sthib /* Update a request cache entry after the rpc has been done */ 220df930be7Sderaadt void 2219a61f037Sthib nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, 2229a61f037Sthib struct mbuf *repmbuf) 223df930be7Sderaadt { 22404b702e4Sericj struct nfsrvcache *rp; 225df930be7Sderaadt 22678530d46Smickey if (!nd->nd_nam2) 227df930be7Sderaadt return; 228b2458ef5Sthib 229b2458ef5Sthib rp = nfsrv_lookupcache(nd); 230b2458ef5Sthib if (rp) { 2316d8dfc9aSthib nfsrv_cleanentry(rp); 232df930be7Sderaadt rp->rc_state = RC_DONE; 233df930be7Sderaadt /* 234df930be7Sderaadt * If we have a valid reply update status and save 235df930be7Sderaadt * the reply for non-idempotent rpc's. 236df930be7Sderaadt */ 237df930be7Sderaadt if (repvalid && nonidempotent[nd->nd_procnum]) { 23878530d46Smickey if ((nd->nd_flag & ND_NFSV3) == 0 && 23978530d46Smickey nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 240df930be7Sderaadt rp->rc_status = nd->nd_repstat; 241df930be7Sderaadt rp->rc_flag |= RC_REPSTATUS; 242df930be7Sderaadt } else { 243b2458ef5Sthib rp->rc_reply = m_copym(repmbuf, 0, M_COPYALL, 244b2458ef5Sthib M_WAIT); 245df930be7Sderaadt rp->rc_flag |= RC_REPMBUF; 246df930be7Sderaadt } 247df930be7Sderaadt } 248df930be7Sderaadt rp->rc_flag &= ~RC_LOCKED; 249df930be7Sderaadt if (rp->rc_flag & RC_WANTED) { 250df930be7Sderaadt rp->rc_flag &= ~RC_WANTED; 2519a61f037Sthib wakeup(rp); 252df930be7Sderaadt } 253df930be7Sderaadt return; 254df930be7Sderaadt } 255df930be7Sderaadt } 256df930be7Sderaadt 2579a61f037Sthib /* Clean out the cache. Called when the last nfsd terminates. */ 258df930be7Sderaadt void 2599a61f037Sthib nfsrv_cleancache(void) 260df930be7Sderaadt { 26104b702e4Sericj struct nfsrvcache *rp, *nextrp; 262df930be7Sderaadt 26304b702e4Sericj for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != NULL; rp = nextrp) { 26404b702e4Sericj nextrp = TAILQ_NEXT(rp, rc_lru); 265df930be7Sderaadt LIST_REMOVE(rp, rc_hash); 266df930be7Sderaadt TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 2676d8dfc9aSthib nfsrv_cleanentry(rp); 268bae2bd50Sderaadt free(rp, M_NFSD, sizeof(*rp)); 269df930be7Sderaadt } 270df930be7Sderaadt numnfsrvcache = 0; 271df930be7Sderaadt } 272b2458ef5Sthib 273b2458ef5Sthib struct nfsrvcache * 274b2458ef5Sthib nfsrv_lookupcache(struct nfsrv_descript *nd) 275b2458ef5Sthib { 2769dc9bb81Sdlg struct nfsrvhash *hash; 277b2458ef5Sthib struct nfsrvcache *rp; 278b2458ef5Sthib 2799dc9bb81Sdlg hash = NFSRCHASH(nd->nd_retxid); 280b2458ef5Sthib loop: 2819dc9bb81Sdlg LIST_FOREACH(rp, hash, rc_hash) { 282b2458ef5Sthib if (nd->nd_retxid == rp->rc_xid && 283b2458ef5Sthib nd->nd_procnum == rp->rc_proc && 284b2458ef5Sthib netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { 285b2458ef5Sthib if ((rp->rc_flag & RC_LOCKED)) { 286b2458ef5Sthib rp->rc_flag |= RC_WANTED; 287ae991a59Smpi tsleep_nsec(rp, PZERO - 1, "nfsrc", INFSLP); 288b2458ef5Sthib goto loop; 289b2458ef5Sthib } 290b2458ef5Sthib rp->rc_flag |= RC_LOCKED; 291b2458ef5Sthib return (rp); 292b2458ef5Sthib } 293b2458ef5Sthib } 294b2458ef5Sthib 295b2458ef5Sthib return (NULL); 296b2458ef5Sthib } 297