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