xref: /onnv-gate/usr/src/stand/lib/fs/common/cache.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  *  Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  *  Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  *  This is mostly new code.  Major revisions were made to allow multiple
27*0Sstevel@tonic-gate  *  file systems to share a common cache.  While this consisted primarily
28*0Sstevel@tonic-gate  *  of including a "devid_t" pointer in the hash functions, I also re-
29*0Sstevel@tonic-gate  *  organized everything to eliminate much of the duplicated code that
30*0Sstevel@tonic-gate  *  had existed previously.
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <sys/param.h>
36*0Sstevel@tonic-gate #include <sys/vnode.h>
37*0Sstevel@tonic-gate #include <sys/sysmacros.h>
38*0Sstevel@tonic-gate #include <sys/filep.h>
39*0Sstevel@tonic-gate #include <sys/salib.h>
40*0Sstevel@tonic-gate #include <sys/promif.h>
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate #ifndef	ICACHE_SIZE
43*0Sstevel@tonic-gate /*
44*0Sstevel@tonic-gate  *  These should probably be defined in an architecture-specific header
45*0Sstevel@tonic-gate  *  file.  The values below are analogous to those used in earlier versions
46*0Sstevel@tonic-gate  *  of this module.
47*0Sstevel@tonic-gate  */
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #define	ICACHE_SIZE 350	    /* Max number of I-node in file cache	*/
50*0Sstevel@tonic-gate #define	DCACHE_SIZE 1500    /* Max number of cached directories		*/
51*0Sstevel@tonic-gate #define	BCACHE_SIZE 250	    /* Max number of cached disk blocks		*/
52*0Sstevel@tonic-gate #endif
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate #define	Next 0		    /* Next pointer in Fwd/Bak link		*/
55*0Sstevel@tonic-gate #define	Prev 1		    /* Previous pointer in Fwd/Back links	*/
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate #define	Frst 0		    /* Ptr to first element of a chain		*/
58*0Sstevel@tonic-gate #define	Last 1		    /* Ptr to last element of a chain		*/
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate #define	Hash 2		    /* Offset of hash chain ptrs.		*/
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate typedef struct cache {	    /* Generic cache element:			*/
63*0Sstevel@tonic-gate     struct cache *link[4];  /* .. Fwd/Bak links for hash chain & LRU	*/
64*0Sstevel@tonic-gate     struct cache **chn;	    /* .. Hash chain link			*/
65*0Sstevel@tonic-gate     int		   dev;	    /* .. Device file handle			*/
66*0Sstevel@tonic-gate     void	 *data;	    /* .. Ptr to associated data		*/
67*0Sstevel@tonic-gate     int		  size;	    /* .. Size of cached data			*/
68*0Sstevel@tonic-gate } cache_t;
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate typedef struct head {	    /* Generic cache header:			*/
71*0Sstevel@tonic-gate 	cache_t	   *aged[2];	/* .. LRU list				*/
72*0Sstevel@tonic-gate 	int (*cmp)(cache_t *);	/* .. Ptr to comparison function	*/
73*0Sstevel@tonic-gate 	int	  size;		/* .. Size of "cache" objects		*/
74*0Sstevel@tonic-gate 	int	  maxblks;	/* .. Max number of cached elements	*/
75*0Sstevel@tonic-gate 	int	  count;	/* .. Current number of cached elements	*/
76*0Sstevel@tonic-gate 	int	  hits;		/* .. Total cache hits			*/
77*0Sstevel@tonic-gate 	int	  searches;	/* .. Total searches			*/
78*0Sstevel@tonic-gate 	int	  purges;	/* .. Total purges			*/
79*0Sstevel@tonic-gate } head_t;
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate /* Constructor for cache headers:					*/
82*0Sstevel@tonic-gate #define	cache_head(h, f, t, n) \
83*0Sstevel@tonic-gate 	{{(cache_t *)&h, (cache_t *)&h}, f, sizeof (t), n}
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate int read_opt;		/* Number of times cache was bypassed	*/
86*0Sstevel@tonic-gate static int x_dev;	/* Target device ID saved here!		*/
87*0Sstevel@tonic-gate static int x_len;	/* length of object			*/
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate #define	LOG2(x) \
90*0Sstevel@tonic-gate 	(((x) <= 16)  ?	 4 : /* Yeah, it's ugly.  But it works! */ \
91*0Sstevel@tonic-gate 	(((x) <= 32)  ?  5 : /* .. Binary log should be part of */ \
92*0Sstevel@tonic-gate 	(((x) <= 64)  ?  6 : /* .. the language!		*/ \
93*0Sstevel@tonic-gate 	(((x) <= 128) ?	 7 : 8))))
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate static cache_t *
get_cache(cache_t * cap,head_t * chp)96*0Sstevel@tonic-gate get_cache(cache_t *cap, head_t *chp)
97*0Sstevel@tonic-gate {
98*0Sstevel@tonic-gate 	/*
99*0Sstevel@tonic-gate 	 *  Search cache:
100*0Sstevel@tonic-gate 	 *
101*0Sstevel@tonic-gate 	 *  The caller pass a pointer to the first "cache" object in the current
102*0Sstevel@tonic-gate 	 *  hash chain ["cap"] and a pointer to the corresponding cache header
103*0Sstevel@tonic-gate 	 *  ["chp"].  This routine follows the cache chain until it finds an
104*0Sstevel@tonic-gate 	 *  entry that matches both the current device [as noted in "x_dev"]
105*0Sstevel@tonic-gate 	 *  and the cache-specific comparison ["chp->cmp"].
106*0Sstevel@tonic-gate 	 *
107*0Sstevel@tonic-gate 	 *  Returns the address of the matching cache object or null if there
108*0Sstevel@tonic-gate 	 *  is none.
109*0Sstevel@tonic-gate 	 */
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 	while (cap) {
112*0Sstevel@tonic-gate 		/*
113*0Sstevel@tonic-gate 		 * Check all entries on the cache chain.  We expect
114*0Sstevel@tonic-gate 		 * chains to be relatively short, so we use a simple
115*0Sstevel@tonic-gate 		 * linear search.
116*0Sstevel@tonic-gate 		 */
117*0Sstevel@tonic-gate 		if ((x_dev == cap->dev) && (*chp->cmp)(cap)) {
118*0Sstevel@tonic-gate 			/*
119*0Sstevel@tonic-gate 			 * Found the entry we're looking for! Move it
120*0Sstevel@tonic-gate 			 * to the front of the cache header's LRU list
121*0Sstevel@tonic-gate 			 * before returing its addres to the caller.
122*0Sstevel@tonic-gate 			 */
123*0Sstevel@tonic-gate 			cap->link[Next]->link[Prev] = cap->link[Prev];
124*0Sstevel@tonic-gate 			cap->link[Prev]->link[Next] = cap->link[Next];
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate 			cap->link[Prev] = (cache_t *)chp->aged;
127*0Sstevel@tonic-gate 			cap->link[Next] = chp->aged[Frst];
128*0Sstevel@tonic-gate 			chp->aged[Frst]->link[Prev] = cap;
129*0Sstevel@tonic-gate 			chp->aged[Frst] = cap;
130*0Sstevel@tonic-gate 			chp->hits += 1;
131*0Sstevel@tonic-gate 			break;
132*0Sstevel@tonic-gate 		}
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate 		cap = cap->link[Hash+Next];
135*0Sstevel@tonic-gate 	}
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	chp->searches += 1;
138*0Sstevel@tonic-gate 	return (cap);
139*0Sstevel@tonic-gate }
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate static cache_t *
reclaim_cache(head_t * chp,int dev)142*0Sstevel@tonic-gate reclaim_cache(head_t *chp, int dev)
143*0Sstevel@tonic-gate {
144*0Sstevel@tonic-gate 	/*
145*0Sstevel@tonic-gate 	 * Reclaim a cache element:
146*0Sstevel@tonic-gate 	 *
147*0Sstevel@tonic-gate 	 * This routine is used to: [a] free the oldest element from
148*0Sstevel@tonic-gate 	 * the cache headed at "chp" and return the address of the
149*0Sstevel@tonic-gate 	 * corresponding "cache_t" struct (iff dev == -1), or [b] free all
150*0Sstevel@tonic-gate 	 * elements on the cache headed at "chp" that belong to the
151*0Sstevel@tonic-gate 	 * indicated "dev"ice.
152*0Sstevel@tonic-gate 	 */
153*0Sstevel@tonic-gate 	cache_t *cap, *cxp;
154*0Sstevel@tonic-gate 	cache_t *cpp = (cache_t *)chp;
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	while ((cap = cpp->link[Prev]) != (cache_t *)chp) {
157*0Sstevel@tonic-gate 		/*
158*0Sstevel@tonic-gate 		 * We follow the cache's LRU chain from oldest to
159*0Sstevel@tonic-gate 		 * newest member.  This ensures that we remove only
160*0Sstevel@tonic-gate 		 * the oldest element when we're called with a
161*0Sstevel@tonic-gate 		 * negative "dev" argument.
162*0Sstevel@tonic-gate 		 */
163*0Sstevel@tonic-gate 		if ((dev == -1) || (dev == cap->dev)) {
164*0Sstevel@tonic-gate 			/*
165*0Sstevel@tonic-gate 			 * This is one of the (perhaps the only)
166*0Sstevel@tonic-gate 			 * elements we're supposed to free.  Remove it
167*0Sstevel@tonic-gate 			 * from both the LRU list and its associated
168*0Sstevel@tonic-gate 			 * hash chain.  Then free the data bound the
169*0Sstevel@tonic-gate 			 * the cache_t element and, if "dev" is
170*0Sstevel@tonic-gate 			 * not -1, the element itself!
171*0Sstevel@tonic-gate 			 */
172*0Sstevel@tonic-gate 			cap->link[Prev]->link[Next] = cap->link[Next];
173*0Sstevel@tonic-gate 			cap->link[Next]->link[Prev] = cap->link[Prev];
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 			if ((cxp = cap->link[Hash+Prev]) != 0)
176*0Sstevel@tonic-gate 				cxp->link[Hash+Next] = cap->link[Hash+Next];
177*0Sstevel@tonic-gate 			else
178*0Sstevel@tonic-gate 				*(cap->chn) = cap->link[Hash+Next];
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate 			if ((cxp = cap->link[Hash+Next]) != 0)
181*0Sstevel@tonic-gate 				cxp->link[Hash+Prev] = cap->link[Hash+Prev];
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 			bkmem_free((caddr_t)cap->data, cap->size);
184*0Sstevel@tonic-gate 			if (dev == -1)
185*0Sstevel@tonic-gate 				return (cap);
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 			bkmem_free((caddr_t)cap, chp->size);
188*0Sstevel@tonic-gate 			chp->count -= 1;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 		} else {
191*0Sstevel@tonic-gate 			/*
192*0Sstevel@tonic-gate 			 * Skip this element, it's not one of the
193*0Sstevel@tonic-gate 			 * ones we want to free up.
194*0Sstevel@tonic-gate 			 */
195*0Sstevel@tonic-gate 			cpp = cap;
196*0Sstevel@tonic-gate 		}
197*0Sstevel@tonic-gate 	};
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	return (0);
200*0Sstevel@tonic-gate }
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate static cache_t *
set_cache(cache_t ** ccp,head_t * chp,int noreclaim)203*0Sstevel@tonic-gate set_cache(cache_t **ccp, head_t *chp, int noreclaim)
204*0Sstevel@tonic-gate {
205*0Sstevel@tonic-gate 	/*
206*0Sstevel@tonic-gate 	 *  Install a cache element:
207*0Sstevel@tonic-gate 	 *
208*0Sstevel@tonic-gate 	 *  The caller passes the address of cache descriptor ["chp"] and the
209*0Sstevel@tonic-gate 	 *  hash chain into which the new element is to be linked ["ccp"].  This
210*0Sstevel@tonic-gate 	 *  routine allocates a new cache_t structure (or, if the maximum number
211*0Sstevel@tonic-gate 	 *  of elements has already been allocated, reclaims the oldest element
212*0Sstevel@tonic-gate 	 *  from the cache), links it into the indicated hash chain, and returns
213*0Sstevel@tonic-gate 	 *  its address to the caller.
214*0Sstevel@tonic-gate 	 */
215*0Sstevel@tonic-gate 	cache_t *cap;
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	if ((chp->count < chp->maxblks) &&
218*0Sstevel@tonic-gate 	    (cap = (cache_t *)bkmem_alloc(chp->size))) {
219*0Sstevel@tonic-gate 		/*
220*0Sstevel@tonic-gate 		 * We haven't reached the maximum cache size yet.
221*0Sstevel@tonic-gate 		 * Allocate a new "cache_t" struct to be added to the
222*0Sstevel@tonic-gate 		 * cache.
223*0Sstevel@tonic-gate 		 */
224*0Sstevel@tonic-gate 		chp->count += 1;
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	} else {
227*0Sstevel@tonic-gate 		if (noreclaim)
228*0Sstevel@tonic-gate 			return (NULL);
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 		/*
231*0Sstevel@tonic-gate 		 * Cache is full.  Use the "reclaim_cache" routine to
232*0Sstevel@tonic-gate 		 * remove the oldest element from the cache.  This
233*0Sstevel@tonic-gate 		 * will become the cache_t struct associated with the
234*0Sstevel@tonic-gate 		 * new element.
235*0Sstevel@tonic-gate 		 */
236*0Sstevel@tonic-gate 		cap = reclaim_cache(chp, -1);
237*0Sstevel@tonic-gate 		chp->purges += 1;
238*0Sstevel@tonic-gate 	}
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	bzero((char *)cap, chp->size);
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	cap->chn = ccp;
243*0Sstevel@tonic-gate 	cap->link[Prev] = (cache_t *)chp;
244*0Sstevel@tonic-gate 	cap->link[Next] = chp->aged[Frst];
245*0Sstevel@tonic-gate 	cap->link[Prev]->link[Next] = cap->link[Next]->link[Prev] = cap;
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	if ((cap->link[Hash+Next] = *ccp) != 0)
248*0Sstevel@tonic-gate 		(*ccp)->link[Hash+Prev] = cap;
249*0Sstevel@tonic-gate 	return (*ccp = cap);
250*0Sstevel@tonic-gate }
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate /*
253*0Sstevel@tonic-gate  *  The File Cache:
254*0Sstevel@tonic-gate  *
255*0Sstevel@tonic-gate  *  This cache (also known as the inode cache) is used to keep track of all
256*0Sstevel@tonic-gate  *  files open on a given device.  The only special data required to locate
257*0Sstevel@tonic-gate  *  a cache entry is the file reference number which is file-system dependent
258*0Sstevel@tonic-gate  *  (for UNIX file systems, it's an inode number).
259*0Sstevel@tonic-gate  */
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate typedef struct icache {		/* Inode cache element:		*/
262*0Sstevel@tonic-gate 	cache_t ic_hdr;		/* .. Standard header		*/
263*0Sstevel@tonic-gate 	int	ic_num;		/* .. I-node number		*/
264*0Sstevel@tonic-gate } ic_t;
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate #define	IC_MAX_HDRS (1 << LOG2(ICACHE_SIZE/6))
267*0Sstevel@tonic-gate #define	IC_HASH(d, i) (((d) + (i)) & (IC_MAX_HDRS - 1))
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate static int x_inode;
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate static int		    /* Cache search predicate:			    */
cmp_icache(cache_t * p)272*0Sstevel@tonic-gate cmp_icache(cache_t *p)
273*0Sstevel@tonic-gate {
274*0Sstevel@tonic-gate 	/* Just check the file number ("x_inode") ...	*/
275*0Sstevel@tonic-gate 	return (((ic_t *)p)->ic_num == x_inode);
276*0Sstevel@tonic-gate }
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate static head_t	ic_head = cache_head(ic_head, cmp_icache, ic_t, ICACHE_SIZE);
279*0Sstevel@tonic-gate static cache_t *ic_hash[IC_MAX_HDRS];
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate void *
get_icache(int dev,int inum)282*0Sstevel@tonic-gate get_icache(int dev, int inum)
283*0Sstevel@tonic-gate {
284*0Sstevel@tonic-gate 	/*
285*0Sstevel@tonic-gate 	 *  Search File Cache:
286*0Sstevel@tonic-gate 	 *
287*0Sstevel@tonic-gate 	 *  This routine searches the file cache looking for the entry bound to
288*0Sstevel@tonic-gate 	 *  the given "dev"ice and file number ["inum"].  If said entry exists,
289*0Sstevel@tonic-gate 	 *  it returns the address of the associated file structure.  Otherwise
290*0Sstevel@tonic-gate 	 *  it returns null.
291*0Sstevel@tonic-gate 	 */
292*0Sstevel@tonic-gate 	cache_t *icp;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	x_dev = dev;
295*0Sstevel@tonic-gate 	x_inode = inum;
296*0Sstevel@tonic-gate 	icp = get_cache(ic_hash[IC_HASH(dev, inum)], &ic_head);
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	return (icp ? (caddr_t)icp->data : 0);
299*0Sstevel@tonic-gate }
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate void
set_icache(int dev,int inum,void * ip,int size)302*0Sstevel@tonic-gate set_icache(int dev, int inum, void *ip, int size)
303*0Sstevel@tonic-gate {
304*0Sstevel@tonic-gate 	/*
305*0Sstevel@tonic-gate 	 *  Build a File Cache Entry:
306*0Sstevel@tonic-gate 	 *
307*0Sstevel@tonic-gate 	 * This routne installs the "size"-byte file structure at
308*0Sstevel@tonic-gate 	 * "*ip" in the inode cache where it may be retrieved by
309*0Sstevel@tonic-gate 	 * subsequent call to get_icache.
310*0Sstevel@tonic-gate 	 */
311*0Sstevel@tonic-gate 	ic_t *icp = (ic_t *)set_cache(&ic_hash[IC_HASH(dev, inum)],
312*0Sstevel@tonic-gate 								&ic_head, 0);
313*0Sstevel@tonic-gate 	icp->ic_num = inum;
314*0Sstevel@tonic-gate 	icp->ic_hdr.data = ip;
315*0Sstevel@tonic-gate 	icp->ic_hdr.dev = dev;
316*0Sstevel@tonic-gate 	icp->ic_hdr.size = size;
317*0Sstevel@tonic-gate }
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate int
set_ricache(int dev,int inum,void * ip,int size)320*0Sstevel@tonic-gate set_ricache(int dev, int inum, void *ip, int size)
321*0Sstevel@tonic-gate {
322*0Sstevel@tonic-gate 	/*
323*0Sstevel@tonic-gate 	 * Reliably set the icache
324*0Sstevel@tonic-gate 	 *
325*0Sstevel@tonic-gate 	 * This routine is the same as set_icache except that it
326*0Sstevel@tonic-gate 	 * will return 1 if the entry could not be entered into the cache
327*0Sstevel@tonic-gate 	 * without a purge.
328*0Sstevel@tonic-gate 	 */
329*0Sstevel@tonic-gate 	ic_t *icp = (ic_t *)set_cache(&ic_hash[IC_HASH(dev, inum)],
330*0Sstevel@tonic-gate 					&ic_head, 1);
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	if (icp == NULL)
333*0Sstevel@tonic-gate 		return (1);
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 	icp->ic_num = inum;
336*0Sstevel@tonic-gate 	icp->ic_hdr.data = ip;
337*0Sstevel@tonic-gate 	icp->ic_hdr.dev = dev;
338*0Sstevel@tonic-gate 	icp->ic_hdr.size = size;
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	return (0);
341*0Sstevel@tonic-gate }
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate /*
344*0Sstevel@tonic-gate  *  The Directory Cache:
345*0Sstevel@tonic-gate  *
346*0Sstevel@tonic-gate  *  This cache is designed to speed directory searches.	 Each entry cor-
347*0Sstevel@tonic-gate  *  responds to a directory entry that was used in a pathname resolution.
348*0Sstevel@tonic-gate  *  The idea is that most files used by the boot wil be contained in a hand-
349*0Sstevel@tonic-gate  *  full of directories, so we can speed searches if we know ahead of time
350*0Sstevel@tonic-gate  *  just where these directories are.
351*0Sstevel@tonic-gate  */
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate typedef struct dcache {		/* Directory cache objects:	*/
354*0Sstevel@tonic-gate 	cache_t dc_hdr;		/* .. Standard header		*/
355*0Sstevel@tonic-gate 	int	dc_inum;	/* .. File number		*/
356*0Sstevel@tonic-gate 	int	dc_pnum;	/* .. Parent diretory's file number */
357*0Sstevel@tonic-gate } dc_t;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate #define	DC_MAX_HDRS (1 << LOG2(DCACHE_SIZE/6))
360*0Sstevel@tonic-gate #define	DC_HASH(d, n, l) (((d) + (n)[0] + (n)[(l)-1] + (l)) & (DC_MAX_HDRS-1))
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate static char *x_name;
363*0Sstevel@tonic-gate static int x_pnum;
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate static int
cmp_dcache(cache_t * p)366*0Sstevel@tonic-gate cmp_dcache(cache_t *p) /* Cache Search predicate:	*/
367*0Sstevel@tonic-gate {
368*0Sstevel@tonic-gate 	/* Check name, length, and parent's file number	*/
369*0Sstevel@tonic-gate 	return ((x_len == p->size) && (x_pnum == ((dc_t *)p)->dc_pnum) &&
370*0Sstevel@tonic-gate 	    (strcmp((char *)p->data, x_name) == 0));
371*0Sstevel@tonic-gate }
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate static head_t	dc_head = cache_head(dc_head, cmp_dcache, dc_t, DCACHE_SIZE);
374*0Sstevel@tonic-gate static cache_t *dc_hash[DC_MAX_HDRS];
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate int
get_dcache(int dev,char * name,int pnum)377*0Sstevel@tonic-gate get_dcache(int dev, char *name, int pnum)
378*0Sstevel@tonic-gate {
379*0Sstevel@tonic-gate 	/*
380*0Sstevel@tonic-gate 	 *  Search Directory Cache:
381*0Sstevel@tonic-gate 	 *
382*0Sstevel@tonic-gate 	 *  This routine searches the directory cache for an entry
383*0Sstevel@tonic-gate 	 *  associated with directory number "pnum" from the given
384*0Sstevel@tonic-gate 	 *  file system that de-scribes a file of the given "name".
385*0Sstevel@tonic-gate 	 *  If we find such an entry, we return the corresponding file
386*0Sstevel@tonic-gate 	 *  number, 0 otherwise.
387*0Sstevel@tonic-gate 	 */
388*0Sstevel@tonic-gate 	dc_t *dcp;
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate 	x_dev = dev;
391*0Sstevel@tonic-gate 	x_len = strlen(name)+1;
392*0Sstevel@tonic-gate 	x_pnum = pnum;
393*0Sstevel@tonic-gate 	x_name = name;
394*0Sstevel@tonic-gate 	dcp = (dc_t *)get_cache(dc_hash[DC_HASH(dev, name, x_len)], &dc_head);
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	return (dcp ? dcp->dc_inum : 0);
397*0Sstevel@tonic-gate }
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate void
set_dcache(int dev,char * name,int pnum,int inum)400*0Sstevel@tonic-gate set_dcache(int dev, char *name, int pnum, int inum)
401*0Sstevel@tonic-gate {
402*0Sstevel@tonic-gate 	/*
403*0Sstevel@tonic-gate 	 *  Build Directory Cache Entry:
404*0Sstevel@tonic-gate 	 *
405*0Sstevel@tonic-gate 	 *  This routine creates directory cache entries to be retrieved later
406*0Sstevel@tonic-gate 	 *  via "get_dcache".  The cache key is composed of three parts: The
407*0Sstevel@tonic-gate 	 *  device specifier, the file name ("name"), and the file number of
408*0Sstevel@tonic-gate 	 *  the directory containing that name ("pnum").  The data portion of
409*0Sstevel@tonic-gate 	 *  the entry consists of the file number ("inum").
410*0Sstevel@tonic-gate 	 */
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 	int len = strlen(name)+1;
413*0Sstevel@tonic-gate 	dc_t *dcp =
414*0Sstevel@tonic-gate 	    (dc_t *)set_cache(&dc_hash[DC_HASH(dev, name, len)], &dc_head, 0);
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	if (dcp->dc_hdr.data = (void *)bkmem_alloc(len)) {
417*0Sstevel@tonic-gate 		/*
418*0Sstevel@tonic-gate 		 * Allocate a buffer for the pathname component, and
419*0Sstevel@tonic-gate 		 * make this the "data" portion of the generalize
420*0Sstevel@tonic-gate 		 * "cache_t" struct. Also fill in the cache-specific
421*0Sstevel@tonic-gate 		 * fields (pnum, inum).
422*0Sstevel@tonic-gate 		 */
423*0Sstevel@tonic-gate 		dcp->dc_pnum = pnum;
424*0Sstevel@tonic-gate 		dcp->dc_inum = inum;
425*0Sstevel@tonic-gate 		dcp->dc_hdr.dev = dev;
426*0Sstevel@tonic-gate 		dcp->dc_hdr.size = len;
427*0Sstevel@tonic-gate 		bcopy(name, (char *)dcp->dc_hdr.data, len);
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	} else {
430*0Sstevel@tonic-gate 		/*
431*0Sstevel@tonic-gate 		 * Not enough memory to make a copy of the name!
432*0Sstevel@tonic-gate 		 * There's probably not enough to do much else either!
433*0Sstevel@tonic-gate 		 */
434*0Sstevel@tonic-gate 		prom_panic("no memory for directory cache");
435*0Sstevel@tonic-gate 	}
436*0Sstevel@tonic-gate }
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate int
set_rdcache(int dev,char * name,int pnum,int inum)439*0Sstevel@tonic-gate set_rdcache(int dev, char *name, int pnum, int inum)
440*0Sstevel@tonic-gate {
441*0Sstevel@tonic-gate 	/*
442*0Sstevel@tonic-gate 	 * Reliably set the dcache
443*0Sstevel@tonic-gate 	 *
444*0Sstevel@tonic-gate 	 * This routine is the same as set_dcache except that it
445*0Sstevel@tonic-gate 	 * return 1 if the entry could not be entered into
446*0Sstevel@tonic-gate 	 * the cache without a purge.
447*0Sstevel@tonic-gate 	 */
448*0Sstevel@tonic-gate 	int len = strlen(name) + 1;
449*0Sstevel@tonic-gate 	dc_t *dcp =
450*0Sstevel@tonic-gate 		(dc_t *)set_cache(&dc_hash[DC_HASH(dev, name, len)],
451*0Sstevel@tonic-gate 								&dc_head, 1);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	if (dcp == NULL)
454*0Sstevel@tonic-gate 		return (1);
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	if ((dcp->dc_hdr.data = (void *)bkmem_alloc(len)) == NULL) {
457*0Sstevel@tonic-gate 		/*
458*0Sstevel@tonic-gate 		 * Not enough memory to make a copy of the name!
459*0Sstevel@tonic-gate 		 * There's probably not enough to do much else either!
460*0Sstevel@tonic-gate 		 */
461*0Sstevel@tonic-gate 		prom_panic("no memory for directory cache");
462*0Sstevel@tonic-gate 		/* NOTREACHED */
463*0Sstevel@tonic-gate 	}
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	/*
466*0Sstevel@tonic-gate 	 * Allocate a buffer for the pathname component, and
467*0Sstevel@tonic-gate 	 * make this the "data" portion of the generalize
468*0Sstevel@tonic-gate 	 * "cache_t" struct. Also fill in the cache-specific
469*0Sstevel@tonic-gate 	 * fields (pnum, inum).
470*0Sstevel@tonic-gate 	 */
471*0Sstevel@tonic-gate 	dcp->dc_pnum = pnum;
472*0Sstevel@tonic-gate 	dcp->dc_inum = inum;
473*0Sstevel@tonic-gate 	dcp->dc_hdr.dev = dev;
474*0Sstevel@tonic-gate 	dcp->dc_hdr.size = len;
475*0Sstevel@tonic-gate 	bcopy(name, (char *)dcp->dc_hdr.data, len);
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 	return (0);
478*0Sstevel@tonic-gate }
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate /*
481*0Sstevel@tonic-gate  *  Disk Block Cache:
482*0Sstevel@tonic-gate  */
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate typedef struct bcache {	    /* Disk block cache objects:		*/
485*0Sstevel@tonic-gate 	cache_t		bc_hdr;	/* .. Standard header			*/
486*0Sstevel@tonic-gate 	unsigned long	bc_blk;	/* .. The block number			*/
487*0Sstevel@tonic-gate } bc_t;
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate #define	BC_MAX_HDRS (1 << LOG2(BCACHE_SIZE/6))
490*0Sstevel@tonic-gate #define	BC_HASH(d, b, l) (((d) + (b) + ((l) >> 8)) & (BC_MAX_HDRS-1))
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate static unsigned long x_blkno;
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate static int
cmp_bcache(cache_t * p)495*0Sstevel@tonic-gate cmp_bcache(cache_t *p) /* Cache Search predicate:		*/
496*0Sstevel@tonic-gate {
497*0Sstevel@tonic-gate 	/* Check block number, buffer size	*/
498*0Sstevel@tonic-gate 	return ((x_len == p->size) && (x_blkno == ((bc_t *)p)->bc_blk));
499*0Sstevel@tonic-gate }
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate static head_t	bc_head = cache_head(bc_head, cmp_bcache, bc_t, BCACHE_SIZE);
502*0Sstevel@tonic-gate static cache_t *bc_hash[BC_MAX_HDRS];
503*0Sstevel@tonic-gate 
504*0Sstevel@tonic-gate caddr_t
get_bcache(fileid_t * fp)505*0Sstevel@tonic-gate get_bcache(fileid_t *fp)
506*0Sstevel@tonic-gate {
507*0Sstevel@tonic-gate 	/*
508*0Sstevel@tonic-gate 	 *  Search Disk Block Cache:
509*0Sstevel@tonic-gate 	 *
510*0Sstevel@tonic-gate 	 *  This should be getting pretty monotonous by now.  Aren't generalized
511*0Sstevel@tonic-gate 	 *  subroutines ("objects", if you prefer) great?
512*0Sstevel@tonic-gate 	 */
513*0Sstevel@tonic-gate 	cache_t *bcp;
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	x_len = fp->fi_count;
516*0Sstevel@tonic-gate 	x_blkno = fp->fi_blocknum;
517*0Sstevel@tonic-gate 	x_dev = fp->fi_devp->di_dcookie;
518*0Sstevel@tonic-gate 	bcp = get_cache(bc_hash[BC_HASH(x_dev, x_blkno, x_len)], &bc_head);
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	return (bcp ? (caddr_t)bcp->data : 0);
521*0Sstevel@tonic-gate }
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate int
set_bcache(fileid_t * fp)524*0Sstevel@tonic-gate set_bcache(fileid_t *fp)
525*0Sstevel@tonic-gate {
526*0Sstevel@tonic-gate 	/*
527*0Sstevel@tonic-gate 	 *  Insert Disk Block Cache Entry:
528*0Sstevel@tonic-gate 	 *
529*0Sstevel@tonic-gate 	 *  In this case, we actually read the requested block into a
530*0Sstevel@tonic-gate 	 *  dynamically allocated buffer before inserting it into the
531*0Sstevel@tonic-gate 	 *  cache.  If the read fails, we return a non-zero value.
532*0Sstevel@tonic-gate 	 *
533*0Sstevel@tonic-gate 	 *  The search keys for disk blocks are the block number and
534*0Sstevel@tonic-gate 	 *  buffer size.  The data associated with each entry is the
535*0Sstevel@tonic-gate 	 *  corresponding data buffer.
536*0Sstevel@tonic-gate 	 */
537*0Sstevel@tonic-gate 	bc_t *bcp;
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 	if (fp->fi_memp = bkmem_alloc(x_len = fp->fi_count)) {
540*0Sstevel@tonic-gate 		/*
541*0Sstevel@tonic-gate 		 *  We were able to succesffully allocate an input
542*0Sstevel@tonic-gate 		 *  buffer, now read the data into it.
543*0Sstevel@tonic-gate 		 */
544*0Sstevel@tonic-gate 		if (diskread(fp) != 0) {
545*0Sstevel@tonic-gate 			/*
546*0Sstevel@tonic-gate 			 * I/O error on read. Free the input buffer,
547*0Sstevel@tonic-gate 			 * print an error message, and bail out.
548*0Sstevel@tonic-gate 			 */
549*0Sstevel@tonic-gate 			bkmem_free(fp->fi_memp, x_len);
550*0Sstevel@tonic-gate 			printf("disk read error\n");
551*0Sstevel@tonic-gate 			return (-1);
552*0Sstevel@tonic-gate 		}
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate 		x_blkno = fp->fi_blocknum;
555*0Sstevel@tonic-gate 		x_dev = fp->fi_devp->di_dcookie;
556*0Sstevel@tonic-gate 		bcp = (bc_t *)
557*0Sstevel@tonic-gate 			set_cache(&bc_hash[BC_HASH(x_dev, x_blkno, x_len)],
558*0Sstevel@tonic-gate 								&bc_head, 0);
559*0Sstevel@tonic-gate 		bcp->bc_blk = x_blkno;
560*0Sstevel@tonic-gate 		bcp->bc_hdr.dev = x_dev;
561*0Sstevel@tonic-gate 		bcp->bc_hdr.size = x_len;
562*0Sstevel@tonic-gate 		bcp->bc_hdr.data = (void *)fp->fi_memp;
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate 	} else {
565*0Sstevel@tonic-gate 		/*
566*0Sstevel@tonic-gate 		 * We could be a bit more convervative here by
567*0Sstevel@tonic-gate 		 * calling "set_cache" before we try to allocate a
568*0Sstevel@tonic-gate 		 * buffer (thereby giving us a chance to re-use a
569*0Sstevel@tonic-gate 		 * previously allocated buffer) but the error recovery
570*0Sstevel@tonic-gate 		 * is a bit trickier, and if we're that short on memory
571*0Sstevel@tonic-gate 		 * we'll have trouble elsewhere anyway!
572*0Sstevel@tonic-gate 		 */
573*0Sstevel@tonic-gate 		prom_panic("can't read - no memory");
574*0Sstevel@tonic-gate 	}
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate 	return (0);
577*0Sstevel@tonic-gate }
578*0Sstevel@tonic-gate 
579*0Sstevel@tonic-gate void
release_cache(int dev)580*0Sstevel@tonic-gate release_cache(int dev)
581*0Sstevel@tonic-gate {
582*0Sstevel@tonic-gate 	/*
583*0Sstevel@tonic-gate 	 *  Reclaim all cache entries:
584*0Sstevel@tonic-gate 	 *
585*0Sstevel@tonic-gate 	 *  This routine is called by the file-system's "closeall" method.  It
586*0Sstevel@tonic-gate 	 *  removes all cache entries associated with that file system from the
587*0Sstevel@tonic-gate 	 *  global cache and release any resources bound to said entrires.
588*0Sstevel@tonic-gate 	 */
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 	(void) reclaim_cache(&ic_head, dev);
591*0Sstevel@tonic-gate 	(void) reclaim_cache(&dc_head, dev);
592*0Sstevel@tonic-gate 	(void) reclaim_cache(&bc_head, dev);
593*0Sstevel@tonic-gate }
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate void
print_cache_data()596*0Sstevel@tonic-gate print_cache_data()
597*0Sstevel@tonic-gate {
598*0Sstevel@tonic-gate 	/*
599*0Sstevel@tonic-gate 	 *  Print some cacheing statistics ...
600*0Sstevel@tonic-gate 	 */
601*0Sstevel@tonic-gate 	static char	*tag[] = { "inode", "directory", "disk block", 0};
602*0Sstevel@tonic-gate 	static head_t	*hdp[] = { &ic_head, &dc_head, &bc_head, 0};
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	int j;
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 	for (j = 0; tag[j]; j++) {
607*0Sstevel@tonic-gate 		/*
608*0Sstevel@tonic-gate 		 * Print statistics maintained in the header
609*0Sstevel@tonic-gate 		 * ("head_t" struct) of each of the above caches.
610*0Sstevel@tonic-gate 		 */
611*0Sstevel@tonic-gate 		head_t *hp = hdp[j];
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 		if (j)
614*0Sstevel@tonic-gate 			printf("\n");
615*0Sstevel@tonic-gate 		printf("%s cache:\n", tag[j]);
616*0Sstevel@tonic-gate 		printf("   max size %d\n", hp->maxblks);
617*0Sstevel@tonic-gate 		printf("   actual size %d\n", hp->count);
618*0Sstevel@tonic-gate 		printf("   total searches %d\n", hp->searches);
619*0Sstevel@tonic-gate 		printf("   cache hits %d\n", hp->hits);
620*0Sstevel@tonic-gate 		printf("   cache purges %d\n", hp->purges);
621*0Sstevel@tonic-gate 	}
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 	printf("\nread opts %d\n", read_opt);
624*0Sstevel@tonic-gate }
625