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