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 * 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 * 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 * 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: */ 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 * 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 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 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 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 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 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 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 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 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 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 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 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