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