139742Smckusick /*
263234Sbostic * Copyright (c) 1989, 1993
363234Sbostic * The Regents of the University of California. All rights reserved.
439742Smckusick *
539742Smckusick * This code is derived from software contributed to Berkeley by
639742Smckusick * Rick Macklem at The University of Guelph.
739742Smckusick *
844512Sbostic * %sccs.include.redist.c%
939742Smckusick *
10*68653Smckusick * @(#)nfs_srvcache.c 8.3 (Berkeley) 03/30/95
1139742Smckusick */
1239742Smckusick
1343431Smckusick /*
1443431Smckusick * Reference: Chet Juszczak, "Improving the Performance and Correctness
1552196Smckusick * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
1652196Smckusick * pages 53-63. San Diego, February 1989.
1743431Smckusick */
1854874Smckusick #include <sys/param.h>
1954874Smckusick #include <sys/vnode.h>
2054874Smckusick #include <sys/mount.h>
2154874Smckusick #include <sys/kernel.h>
2254874Smckusick #include <sys/systm.h>
2354874Smckusick #include <sys/proc.h>
2454874Smckusick #include <sys/mbuf.h>
2555520Smckusick #include <sys/malloc.h>
2654874Smckusick #include <sys/socket.h>
2754874Smckusick #include <sys/socketvar.h>
2856535Sbostic
2954874Smckusick #include <netinet/in.h>
3052196Smckusick #ifdef ISO
3154874Smckusick #include <netiso/iso.h>
3252196Smckusick #endif
3354874Smckusick #include <nfs/nfsm_subs.h>
3454874Smckusick #include <nfs/rpcv2.h>
35*68653Smckusick #include <nfs/nfsproto.h>
3654874Smckusick #include <nfs/nfs.h>
3754874Smckusick #include <nfs/nfsrvcache.h>
3854874Smckusick #include <nfs/nqnfs.h>
3939742Smckusick
40*68653Smckusick extern struct nfsstats nfsstats;
41*68653Smckusick extern int nfsv2_procid[NFS_NPROCS];
4255520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
4339742Smckusick
4467708Smckusick #define NFSRCHASH(xid) \
4567708Smckusick (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
4667708Smckusick LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
4767708Smckusick TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
4867708Smckusick u_long nfsrvhash;
4939742Smckusick
5039742Smckusick #define TRUE 1
5139742Smckusick #define FALSE 0
5239742Smckusick
5352196Smckusick #define NETFAMILY(rp) \
5452196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
5552196Smckusick
5639742Smckusick /*
5739742Smckusick * Static array that defines which nfs rpc's are nonidempotent
5839742Smckusick */
5940118Smckusick int nonidempotent[NFS_NPROCS] = {
6039742Smckusick FALSE,
6139742Smckusick FALSE,
6239742Smckusick TRUE,
6339742Smckusick FALSE,
6439742Smckusick FALSE,
6539742Smckusick FALSE,
6639742Smckusick FALSE,
6739742Smckusick TRUE,
6839742Smckusick TRUE,
6939742Smckusick TRUE,
7039742Smckusick TRUE,
7139742Smckusick TRUE,
7239742Smckusick TRUE,
7339742Smckusick TRUE,
7439742Smckusick TRUE,
75*68653Smckusick TRUE,
7639742Smckusick FALSE,
7739742Smckusick FALSE,
7852196Smckusick FALSE,
7952196Smckusick FALSE,
8052196Smckusick FALSE,
8152196Smckusick FALSE,
8256362Smckusick FALSE,
83*68653Smckusick FALSE,
84*68653Smckusick FALSE,
85*68653Smckusick FALSE,
8639742Smckusick };
8739742Smckusick
8839742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
89*68653Smckusick static int nfsv2_repstat[NFS_NPROCS] = {
9039742Smckusick FALSE,
9139742Smckusick FALSE,
9239742Smckusick FALSE,
9339742Smckusick FALSE,
9439742Smckusick FALSE,
9539742Smckusick FALSE,
9639742Smckusick FALSE,
9739742Smckusick FALSE,
9839742Smckusick FALSE,
9939742Smckusick FALSE,
10039742Smckusick TRUE,
10139742Smckusick TRUE,
10239742Smckusick TRUE,
10339742Smckusick TRUE,
10439742Smckusick FALSE,
10539742Smckusick TRUE,
10639742Smckusick FALSE,
10739742Smckusick FALSE,
10839742Smckusick };
10939742Smckusick
11039742Smckusick /*
11139742Smckusick * Initialize the server request cache list
11239742Smckusick */
113*68653Smckusick void
nfsrv_initcache()11439742Smckusick nfsrv_initcache()
11539742Smckusick {
11639742Smckusick
11767708Smckusick nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
11867708Smckusick TAILQ_INIT(&nfsrvlruhead);
11939742Smckusick }
12039742Smckusick
12139742Smckusick /*
12239742Smckusick * Look for the request in the cache
12339742Smckusick * If found then
12439742Smckusick * return action and optionally reply
12539742Smckusick * else
12639742Smckusick * insert it in the cache
12739742Smckusick *
12839742Smckusick * The rules are as follows:
12939742Smckusick * - if in progress, return DROP request
13039742Smckusick * - if completed within DELAY of the current time, return DROP it
13139742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or
13239742Smckusick * return DOIT
13339742Smckusick * Update/add new request at end of lru list
13439742Smckusick */
135*68653Smckusick int
nfsrv_getcache(nd,slp,repp)136*68653Smckusick nfsrv_getcache(nd, slp, repp)
137*68653Smckusick register struct nfsrv_descript *nd;
138*68653Smckusick struct nfssvc_sock *slp;
13939742Smckusick struct mbuf **repp;
14039742Smckusick {
14167708Smckusick register struct nfsrvcache *rp;
14239742Smckusick struct mbuf *mb;
14352196Smckusick struct sockaddr_in *saddr;
14439742Smckusick caddr_t bpos;
14539742Smckusick int ret;
14639742Smckusick
147*68653Smckusick /*
148*68653Smckusick * Don't cache recent requests for reliable transport protocols.
149*68653Smckusick * (Maybe we should for the case of a reconnect, but..)
150*68653Smckusick */
151*68653Smckusick if (!nd->nd_nam2)
15252196Smckusick return (RC_DOIT);
15339742Smckusick loop:
15467708Smckusick for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
15567708Smckusick rp = rp->rc_hash.le_next) {
15652196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
157*68653Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
15839742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) {
15939742Smckusick rp->rc_flag |= RC_WANTED;
16043352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
16139742Smckusick goto loop;
16239742Smckusick }
16339742Smckusick rp->rc_flag |= RC_LOCKED;
16455520Smckusick /* If not at end of LRU chain, move it there */
16567708Smckusick if (rp->rc_lru.tqe_next) {
16667708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
16767708Smckusick TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
16852196Smckusick }
16939742Smckusick if (rp->rc_state == RC_UNUSED)
17039742Smckusick panic("nfsrv cache");
17160155Smckusick if (rp->rc_state == RC_INPROG) {
17239742Smckusick nfsstats.srvcache_inproghits++;
17339742Smckusick ret = RC_DROPIT;
17439742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) {
17560155Smckusick nfsstats.srvcache_nonidemdonehits++;
176*68653Smckusick nfs_rephead(0, nd, slp, rp->rc_status,
17752196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos);
17839742Smckusick ret = RC_REPLY;
17939742Smckusick } else if (rp->rc_flag & RC_REPMBUF) {
18060155Smckusick nfsstats.srvcache_nonidemdonehits++;
18141901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18239742Smckusick M_WAIT);
18339742Smckusick ret = RC_REPLY;
18439742Smckusick } else {
18560155Smckusick nfsstats.srvcache_idemdonehits++;
18639742Smckusick rp->rc_state = RC_INPROG;
18739742Smckusick ret = RC_DOIT;
18839742Smckusick }
18939742Smckusick rp->rc_flag &= ~RC_LOCKED;
19039742Smckusick if (rp->rc_flag & RC_WANTED) {
19139742Smckusick rp->rc_flag &= ~RC_WANTED;
19239742Smckusick wakeup((caddr_t)rp);
19339742Smckusick }
19439742Smckusick return (ret);
19539742Smckusick }
19639742Smckusick }
19739742Smckusick nfsstats.srvcache_misses++;
19855520Smckusick if (numnfsrvcache < desirednfsrvcache) {
19955520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
20055520Smckusick M_NFSD, M_WAITOK);
20155520Smckusick bzero((char *)rp, sizeof *rp);
20255520Smckusick numnfsrvcache++;
20355520Smckusick rp->rc_flag = RC_LOCKED;
20455520Smckusick } else {
20567708Smckusick rp = nfsrvlruhead.tqh_first;
20655520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) {
20755520Smckusick rp->rc_flag |= RC_WANTED;
20855520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
20967708Smckusick rp = nfsrvlruhead.tqh_first;
21055520Smckusick }
21155520Smckusick rp->rc_flag |= RC_LOCKED;
21267708Smckusick LIST_REMOVE(rp, rc_hash);
21367708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
21455520Smckusick if (rp->rc_flag & RC_REPMBUF)
21555520Smckusick m_freem(rp->rc_reply);
21655520Smckusick if (rp->rc_flag & RC_NAM)
21755520Smckusick MFREE(rp->rc_nam, mb);
21855520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED);
21952196Smckusick }
22067708Smckusick TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
22139742Smckusick rp->rc_state = RC_INPROG;
22252196Smckusick rp->rc_xid = nd->nd_retxid;
223*68653Smckusick saddr = mtod(nd->nd_nam, struct sockaddr_in *);
22452196Smckusick switch (saddr->sin_family) {
22552196Smckusick case AF_INET:
22652196Smckusick rp->rc_flag |= RC_INETADDR;
22752196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr;
22852196Smckusick break;
22952196Smckusick case AF_ISO:
23052196Smckusick default:
23152196Smckusick rp->rc_flag |= RC_NAM;
232*68653Smckusick rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
23352196Smckusick break;
23452196Smckusick };
23552196Smckusick rp->rc_proc = nd->nd_procnum;
23667708Smckusick LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
23752196Smckusick rp->rc_flag &= ~RC_LOCKED;
23852196Smckusick if (rp->rc_flag & RC_WANTED) {
23952196Smckusick rp->rc_flag &= ~RC_WANTED;
24052196Smckusick wakeup((caddr_t)rp);
24152196Smckusick }
24239742Smckusick return (RC_DOIT);
24339742Smckusick }
24439742Smckusick
24539742Smckusick /*
24639742Smckusick * Update a request cache entry after the rpc has been done
24739742Smckusick */
24852196Smckusick void
nfsrv_updatecache(nd,repvalid,repmbuf)249*68653Smckusick nfsrv_updatecache(nd, repvalid, repmbuf)
250*68653Smckusick register struct nfsrv_descript *nd;
25140253Smckusick int repvalid;
25239742Smckusick struct mbuf *repmbuf;
25339742Smckusick {
25439742Smckusick register struct nfsrvcache *rp;
25539742Smckusick
256*68653Smckusick if (!nd->nd_nam2)
25752196Smckusick return;
25839742Smckusick loop:
25967708Smckusick for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
26067708Smckusick rp = rp->rc_hash.le_next) {
26152196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
262*68653Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
26339742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) {
26439742Smckusick rp->rc_flag |= RC_WANTED;
26543352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
26639742Smckusick goto loop;
26739742Smckusick }
26839742Smckusick rp->rc_flag |= RC_LOCKED;
26939742Smckusick rp->rc_state = RC_DONE;
27040253Smckusick /*
27140253Smckusick * If we have a valid reply update status and save
27241901Smckusick * the reply for non-idempotent rpc's.
27340253Smckusick */
27460155Smckusick if (repvalid && nonidempotent[nd->nd_procnum]) {
275*68653Smckusick if ((nd->nd_flag & ND_NFSV3) == 0 &&
276*68653Smckusick nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
27760155Smckusick rp->rc_status = nd->nd_repstat;
27860155Smckusick rp->rc_flag |= RC_REPSTATUS;
27960155Smckusick } else {
28060155Smckusick rp->rc_reply = m_copym(repmbuf,
28160155Smckusick 0, M_COPYALL, M_WAIT);
28260155Smckusick rp->rc_flag |= RC_REPMBUF;
28339742Smckusick }
28439742Smckusick }
28539742Smckusick rp->rc_flag &= ~RC_LOCKED;
28639742Smckusick if (rp->rc_flag & RC_WANTED) {
28739742Smckusick rp->rc_flag &= ~RC_WANTED;
28839742Smckusick wakeup((caddr_t)rp);
28939742Smckusick }
29039742Smckusick return;
29139742Smckusick }
29239742Smckusick }
29339742Smckusick }
29452196Smckusick
29552196Smckusick /*
29652196Smckusick * Clean out the cache. Called when the last nfsd terminates.
29752196Smckusick */
29852196Smckusick void
nfsrv_cleancache()29952196Smckusick nfsrv_cleancache()
30052196Smckusick {
30155520Smckusick register struct nfsrvcache *rp, *nextrp;
30252196Smckusick
30367708Smckusick for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
30467708Smckusick nextrp = rp->rc_lru.tqe_next;
30567708Smckusick LIST_REMOVE(rp, rc_hash);
30667708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
30755520Smckusick free(rp, M_NFSD);
30852196Smckusick }
30955520Smckusick numnfsrvcache = 0;
31052196Smckusick }
311