xref: /csrg-svn/sys/kern/vfs_cache.c (revision 37487)
1*37487Smckusick /*
2*37487Smckusick  * Copyright (c) 1989 The Regents of the University of California.
3*37487Smckusick  * All rights reserved.
4*37487Smckusick  *
5*37487Smckusick  * Redistribution and use in source and binary forms are permitted
6*37487Smckusick  * provided that the above copyright notice and this paragraph are
7*37487Smckusick  * duplicated in all such forms and that any documentation,
8*37487Smckusick  * advertising materials, and other materials related to such
9*37487Smckusick  * distribution and use acknowledge that the software was developed
10*37487Smckusick  * by the University of California, Berkeley.  The name of the
11*37487Smckusick  * University may not be used to endorse or promote products derived
12*37487Smckusick  * from this software without specific prior written permission.
13*37487Smckusick  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*37487Smckusick  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*37487Smckusick  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16*37487Smckusick  *
17*37487Smckusick  *	@(#)vfs_cache.c	7.1 (Berkeley) 04/24/89
18*37487Smckusick  */
19*37487Smckusick 
20*37487Smckusick #include "param.h"
21*37487Smckusick #include "systm.h"
22*37487Smckusick #include "time.h"
23*37487Smckusick #include "vnode.h"
24*37487Smckusick #include "dir.h"
25*37487Smckusick #include "namei.h"
26*37487Smckusick 
27*37487Smckusick /*
28*37487Smckusick  * Name caching works as follows:
29*37487Smckusick  *
30*37487Smckusick  * Names found by directory scans are retained in a cache
31*37487Smckusick  * for future reference.  It is managed LRU, so frequently
32*37487Smckusick  * used names will hang around.  Cache is indexed by hash value
33*37487Smckusick  * obtained from (ino,dev,name) where ino & dev refer to the
34*37487Smckusick  * directory containing name.
35*37487Smckusick  *
36*37487Smckusick  * For simplicity (and economy of storage), names longer than
37*37487Smckusick  * a maximum length of NCHNAMLEN are not cached; they occur
38*37487Smckusick  * infrequently in any case, and are almost never of interest.
39*37487Smckusick  *
40*37487Smckusick  * Upon reaching the last segment of a path, if the reference
41*37487Smckusick  * is for DELETE, or NOCACHE is set (rewrite), and the
42*37487Smckusick  * name is located in the cache, it will be dropped.
43*37487Smckusick  */
44*37487Smckusick 
45*37487Smckusick /*
46*37487Smckusick  * Structures associated with name cacheing.
47*37487Smckusick  */
48*37487Smckusick #define	NCHHASH		128	/* size of hash table */
49*37487Smckusick 
50*37487Smckusick #if	((NCHHASH)&((NCHHASH)-1)) != 0
51*37487Smckusick #define	NHASH(h)	(((unsigned)(h) >> 6) % (NCHHASH))
52*37487Smckusick #else
53*37487Smckusick #define	NHASH(h)	(((unsigned)(h) >> 6) & ((NCHHASH)-1))
54*37487Smckusick #endif
55*37487Smckusick 
56*37487Smckusick union nchash {
57*37487Smckusick 	union	nchash *nch_head[2];
58*37487Smckusick 	struct	namecache *nch_chain[2];
59*37487Smckusick } nchash[NCHHASH];
60*37487Smckusick #define	nch_forw	nch_chain[0]
61*37487Smckusick #define	nch_back	nch_chain[1]
62*37487Smckusick 
63*37487Smckusick struct	namecache *nchhead, **nchtail;	/* LRU chain pointers */
64*37487Smckusick struct	nchstats nchstats;		/* cache effectiveness statistics */
65*37487Smckusick 
66*37487Smckusick /*
67*37487Smckusick  * Look for a the name in the cache. We don't do this
68*37487Smckusick  * if the segment name is long, simply so the cache can avoid
69*37487Smckusick  * holding long names (which would either waste space, or
70*37487Smckusick  * add greatly to the complexity).
71*37487Smckusick  */
72*37487Smckusick struct vnode *
73*37487Smckusick cache_lookup(ndp)
74*37487Smckusick 	register struct nameidata *ndp;
75*37487Smckusick {
76*37487Smckusick 	register struct vnode *dp;
77*37487Smckusick 	register struct namecache *ncp;
78*37487Smckusick 	union nchash *nhp;
79*37487Smckusick 
80*37487Smckusick 	if (ndp->ni_dent.d_namlen > NCHNAMLEN) {
81*37487Smckusick 		nchstats.ncs_long++;
82*37487Smckusick 		ndp->ni_makeentry = 0;
83*37487Smckusick 		return (0);
84*37487Smckusick 	}
85*37487Smckusick 	dp = ndp->ni_vp;
86*37487Smckusick 	nhp = &nchash[NHASH(dp)];
87*37487Smckusick 	for (ncp = nhp->nch_forw; ncp != (struct namecache *)nhp;
88*37487Smckusick 	    ncp = ncp->nc_forw) {
89*37487Smckusick 		if (ncp->nc_vp == dp &&
90*37487Smckusick 		    ncp->nc_nlen == ndp->ni_dent.d_namlen &&
91*37487Smckusick 		    !bcmp(ncp->nc_name, ndp->ni_dent.d_name,
92*37487Smckusick 			(unsigned)ncp->nc_nlen))
93*37487Smckusick 			break;
94*37487Smckusick 	}
95*37487Smckusick 	if (ncp == (struct namecache *)nhp) {
96*37487Smckusick 		nchstats.ncs_miss++;
97*37487Smckusick 		ncp = NULL;
98*37487Smckusick 		return (0);
99*37487Smckusick 	}
100*37487Smckusick 	if (ndp->ni_makeentry) {
101*37487Smckusick 		/*
102*37487Smckusick 		 * move this slot to end of LRU
103*37487Smckusick 		 * chain, if not already there
104*37487Smckusick 		 */
105*37487Smckusick 		if (ncp->nc_nxt) {
106*37487Smckusick 			/* remove from LRU chain */
107*37487Smckusick 			*ncp->nc_prev = ncp->nc_nxt;
108*37487Smckusick 			ncp->nc_nxt->nc_prev = ncp->nc_prev;
109*37487Smckusick 
110*37487Smckusick 			/* and replace at end of it */
111*37487Smckusick 			ncp->nc_nxt = NULL;
112*37487Smckusick 			ncp->nc_prev = nchtail;
113*37487Smckusick 			*nchtail = ncp;
114*37487Smckusick 			nchtail = &ncp->nc_nxt;
115*37487Smckusick 		}
116*37487Smckusick 		/* ndp->ni_dent.d_ino = dp->i_number; */
117*37487Smckusick 		/* ni_dent.d_reclen is garbage ... */
118*37487Smckusick 		nchstats.ncs_goodhits++;
119*37487Smckusick 		return (ncp->nc_vp);
120*37487Smckusick 	}
121*37487Smckusick 
122*37487Smckusick 	/*
123*37487Smckusick 	 * Last component and we are renaming or deleting,
124*37487Smckusick 	 * the cache entry is invalid, or otherwise don't
125*37487Smckusick 	 * want cache entry to exist.
126*37487Smckusick 	 */
127*37487Smckusick 	nchstats.ncs_badhits++;
128*37487Smckusick 	/* remove from LRU chain */
129*37487Smckusick 	*ncp->nc_prev = ncp->nc_nxt;
130*37487Smckusick 	if (ncp->nc_nxt)
131*37487Smckusick 		ncp->nc_nxt->nc_prev = ncp->nc_prev;
132*37487Smckusick 	else
133*37487Smckusick 		nchtail = ncp->nc_prev;
134*37487Smckusick 	remque(ncp);		/* remove from hash chain */
135*37487Smckusick 	/* insert at head of LRU list (first to grab) */
136*37487Smckusick 	ncp->nc_nxt = nchhead;
137*37487Smckusick 	ncp->nc_prev = &nchhead;
138*37487Smckusick 	nchhead->nc_prev = &ncp->nc_nxt;
139*37487Smckusick 	nchhead = ncp;
140*37487Smckusick 	/* and make a dummy hash chain */
141*37487Smckusick 	ncp->nc_forw = ncp;
142*37487Smckusick 	ncp->nc_back = ncp;
143*37487Smckusick 	ncp = NULL;
144*37487Smckusick 	return (0);
145*37487Smckusick }
146*37487Smckusick 
147*37487Smckusick /*
148*37487Smckusick  * Add an entry to the cache
149*37487Smckusick  */
150*37487Smckusick cache_enter(ndp)
151*37487Smckusick 	register struct nameidata *ndp;
152*37487Smckusick {
153*37487Smckusick 	register struct namecache *ncp;
154*37487Smckusick 	union nchash *nhp;
155*37487Smckusick 
156*37487Smckusick 	/*
157*37487Smckusick 	 * Free the cache slot at head of lru chain.
158*37487Smckusick 	 */
159*37487Smckusick 	if (ncp = nchhead) {
160*37487Smckusick 		/* remove from lru chain */
161*37487Smckusick 		*ncp->nc_prev = ncp->nc_nxt;
162*37487Smckusick 		if (ncp->nc_nxt)
163*37487Smckusick 			ncp->nc_nxt->nc_prev = ncp->nc_prev;
164*37487Smckusick 		else
165*37487Smckusick 			nchtail = ncp->nc_prev;
166*37487Smckusick 		remque(ncp);		/* remove from old hash chain */
167*37487Smckusick 		/* grab the inode we just found */
168*37487Smckusick 		ncp->nc_vp = ndp->ni_vp;
169*37487Smckusick 		/* fill in cache info */
170*37487Smckusick 		ncp->nc_dp = ndp->ni_dvp;	/* parents vnode */
171*37487Smckusick 		ncp->nc_nlen = ndp->ni_dent.d_namlen;
172*37487Smckusick 		bcopy(ndp->ni_dent.d_name, ncp->nc_name,
173*37487Smckusick 		    (unsigned)ncp->nc_nlen);
174*37487Smckusick 		/* link at end of lru chain */
175*37487Smckusick 		ncp->nc_nxt = NULL;
176*37487Smckusick 		ncp->nc_prev = nchtail;
177*37487Smckusick 		*nchtail = ncp;
178*37487Smckusick 		nchtail = &ncp->nc_nxt;
179*37487Smckusick 		/* and insert on hash chain */
180*37487Smckusick 		nhp = &nchash[NHASH(ndp->ni_vp)];
181*37487Smckusick 		insque(ncp, nhp);
182*37487Smckusick 	}
183*37487Smckusick }
184*37487Smckusick 
185*37487Smckusick /*
186*37487Smckusick  * Name cache initialization, from main() when we are booting
187*37487Smckusick  */
188*37487Smckusick nchinit()
189*37487Smckusick {
190*37487Smckusick 	register union nchash *nchp;
191*37487Smckusick 	register struct namecache *ncp;
192*37487Smckusick 
193*37487Smckusick 	nchhead = 0;
194*37487Smckusick 	nchtail = &nchhead;
195*37487Smckusick 	for (ncp = namecache; ncp < &namecache[nchsize]; ncp++) {
196*37487Smckusick 		ncp->nc_forw = ncp;			/* hash chain */
197*37487Smckusick 		ncp->nc_back = ncp;
198*37487Smckusick 		ncp->nc_nxt = NULL;			/* lru chain */
199*37487Smckusick 		*nchtail = ncp;
200*37487Smckusick 		ncp->nc_prev = nchtail;
201*37487Smckusick 		nchtail = &ncp->nc_nxt;
202*37487Smckusick 		/* all else is zero already */
203*37487Smckusick 	}
204*37487Smckusick 	for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) {
205*37487Smckusick 		nchp->nch_head[0] = nchp;
206*37487Smckusick 		nchp->nch_head[1] = nchp;
207*37487Smckusick 	}
208*37487Smckusick }
209*37487Smckusick 
210*37487Smckusick /*
211*37487Smckusick  * Cache flush, a particular vnode; called when a vnode is renamed to
212*37487Smckusick  * remove entries that would now be invalid
213*37487Smckusick  *
214*37487Smckusick  * The line "nxtcp = nchhead" near the end is to avoid potential problems
215*37487Smckusick  * if the cache lru chain is modified while we are dumping the
216*37487Smckusick  * inode.  This makes the algorithm O(n^2), but do you think I care?
217*37487Smckusick  */
218*37487Smckusick cache_purge(vp)
219*37487Smckusick 	register struct vnode *vp;
220*37487Smckusick {
221*37487Smckusick 	register struct namecache *ncp, *nxtcp;
222*37487Smckusick 
223*37487Smckusick 	for (ncp = nchhead; ncp; ncp = nxtcp) {
224*37487Smckusick 		nxtcp = ncp->nc_nxt;
225*37487Smckusick 		if (ncp->nc_vp == NULL || ncp->nc_vp != vp)
226*37487Smckusick 			continue;
227*37487Smckusick 		/* free the resources we had */
228*37487Smckusick 		ncp->nc_vp = NULL;
229*37487Smckusick 		ncp->nc_dp = NULL;
230*37487Smckusick 		remque(ncp);		/* remove entry from its hash chain */
231*37487Smckusick 		ncp->nc_forw = ncp;	/* and make a dummy one */
232*37487Smckusick 		ncp->nc_back = ncp;
233*37487Smckusick 		/* delete this entry from LRU chain */
234*37487Smckusick 		*ncp->nc_prev = nxtcp;
235*37487Smckusick 		if (nxtcp)
236*37487Smckusick 			nxtcp->nc_prev = ncp->nc_prev;
237*37487Smckusick 		else
238*37487Smckusick 			nchtail = ncp->nc_prev;
239*37487Smckusick 		/* cause rescan of list, it may have altered */
240*37487Smckusick 		nxtcp = nchhead;
241*37487Smckusick 		/* put the now-free entry at head of LRU */
242*37487Smckusick 		ncp->nc_nxt = nxtcp;
243*37487Smckusick 		ncp->nc_prev = &nchhead;
244*37487Smckusick 		nxtcp->nc_prev = &ncp->nc_nxt;
245*37487Smckusick 		nchhead = ncp;
246*37487Smckusick 	}
247*37487Smckusick }
248*37487Smckusick 
249*37487Smckusick /*
250*37487Smckusick  * Cache flush, a whole filesystem; called when filesys is umounted to
251*37487Smckusick  * remove entries that would now be invalid
252*37487Smckusick  *
253*37487Smckusick  * The line "nxtcp = nchhead" near the end is to avoid potential problems
254*37487Smckusick  * if the cache lru chain is modified while we are dumping the
255*37487Smckusick  * inode.  This makes the algorithm O(n^2), but do you think I care?
256*37487Smckusick  */
257*37487Smckusick cache_purgevfs(mp)
258*37487Smckusick 	register struct mount *mp;
259*37487Smckusick {
260*37487Smckusick 	register struct namecache *ncp, *nxtcp;
261*37487Smckusick 
262*37487Smckusick 	for (ncp = nchhead; ncp; ncp = nxtcp) {
263*37487Smckusick 		nxtcp = ncp->nc_nxt;
264*37487Smckusick 		if (ncp->nc_vp == NULL || ncp->nc_vp->v_mount != mp)
265*37487Smckusick 			continue;
266*37487Smckusick 		/* free the resources we had */
267*37487Smckusick 		ncp->nc_vp = NULL;
268*37487Smckusick 		ncp->nc_dp = NULL;
269*37487Smckusick 		remque(ncp);		/* remove entry from its hash chain */
270*37487Smckusick 		ncp->nc_forw = ncp;	/* and make a dummy one */
271*37487Smckusick 		ncp->nc_back = ncp;
272*37487Smckusick 		/* delete this entry from LRU chain */
273*37487Smckusick 		*ncp->nc_prev = nxtcp;
274*37487Smckusick 		if (nxtcp)
275*37487Smckusick 			nxtcp->nc_prev = ncp->nc_prev;
276*37487Smckusick 		else
277*37487Smckusick 			nchtail = ncp->nc_prev;
278*37487Smckusick 		/* cause rescan of list, it may have altered */
279*37487Smckusick 		nxtcp = nchhead;
280*37487Smckusick 		/* put the now-free entry at head of LRU */
281*37487Smckusick 		ncp->nc_nxt = nxtcp;
282*37487Smckusick 		ncp->nc_prev = &nchhead;
283*37487Smckusick 		nxtcp->nc_prev = &ncp->nc_nxt;
284*37487Smckusick 		nchhead = ncp;
285*37487Smckusick 	}
286*37487Smckusick }
287