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