137487Smckusick /* 237487Smckusick * Copyright (c) 1989 The Regents of the University of California. 337487Smckusick * All rights reserved. 437487Smckusick * 537487Smckusick * Redistribution and use in source and binary forms are permitted 637487Smckusick * provided that the above copyright notice and this paragraph are 737487Smckusick * duplicated in all such forms and that any documentation, 837487Smckusick * advertising materials, and other materials related to such 937487Smckusick * distribution and use acknowledge that the software was developed 1037487Smckusick * by the University of California, Berkeley. The name of the 1137487Smckusick * University may not be used to endorse or promote products derived 1237487Smckusick * from this software without specific prior written permission. 1337487Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1437487Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1537487Smckusick * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1637487Smckusick * 17*38768Smckusick * @(#)vfs_cache.c 7.4 (Berkeley) 08/25/89 1837487Smckusick */ 1937487Smckusick 2037487Smckusick #include "param.h" 2137487Smckusick #include "systm.h" 2237487Smckusick #include "time.h" 2337487Smckusick #include "vnode.h" 2437487Smckusick #include "namei.h" 25*38768Smckusick #include "errno.h" 2637487Smckusick 2737487Smckusick /* 2837487Smckusick * Name caching works as follows: 2937487Smckusick * 3037487Smckusick * Names found by directory scans are retained in a cache 3137487Smckusick * for future reference. It is managed LRU, so frequently 3237487Smckusick * used names will hang around. Cache is indexed by hash value 33*38768Smckusick * obtained from (vp, name) where vp refers to the directory 34*38768Smckusick * containing name. 3537487Smckusick * 3637487Smckusick * For simplicity (and economy of storage), names longer than 3737487Smckusick * a maximum length of NCHNAMLEN are not cached; they occur 3837487Smckusick * infrequently in any case, and are almost never of interest. 3937487Smckusick * 4037487Smckusick * Upon reaching the last segment of a path, if the reference 4137487Smckusick * is for DELETE, or NOCACHE is set (rewrite), and the 4237487Smckusick * name is located in the cache, it will be dropped. 4337487Smckusick */ 4437487Smckusick 4537487Smckusick /* 4637487Smckusick * Structures associated with name cacheing. 4737487Smckusick */ 4837487Smckusick #define NCHHASH 128 /* size of hash table */ 4937487Smckusick 5037487Smckusick #if ((NCHHASH)&((NCHHASH)-1)) != 0 51*38768Smckusick #define NHASH(vp, h) ((((unsigned)(h) >> 6) + (h)) % (NCHHASH)) 5237487Smckusick #else 53*38768Smckusick #define NHASH(vp, h) ((((unsigned)(h) >> 6) + (h)) & ((NCHHASH)-1)) 5437487Smckusick #endif 5537487Smckusick 5637487Smckusick union nchash { 5737487Smckusick union nchash *nch_head[2]; 5837487Smckusick struct namecache *nch_chain[2]; 5937487Smckusick } nchash[NCHHASH]; 6037487Smckusick #define nch_forw nch_chain[0] 6137487Smckusick #define nch_back nch_chain[1] 6237487Smckusick 6337487Smckusick struct namecache *nchhead, **nchtail; /* LRU chain pointers */ 6437487Smckusick struct nchstats nchstats; /* cache effectiveness statistics */ 6537487Smckusick 66*38768Smckusick int doingcache = 1; /* 1 => enable the cache */ 67*38768Smckusick 6837487Smckusick /* 6937487Smckusick * Look for a the name in the cache. We don't do this 7037487Smckusick * if the segment name is long, simply so the cache can avoid 7137487Smckusick * holding long names (which would either waste space, or 7237487Smckusick * add greatly to the complexity). 73*38768Smckusick * 74*38768Smckusick * Lookup is called with ni_dvp pointing to the directory to search, 75*38768Smckusick * ni_ptr pointing to the name of the entry being sought, ni_namelen 76*38768Smckusick * tells the length of the name, and ni_hash contains a hash of 77*38768Smckusick * the name. If the lookup succeeds, the vnode is returned in ni_vp 78*38768Smckusick * and a status of -1 is returned. If the lookup determines that 79*38768Smckusick * the name does not exist (negative cacheing), a status of ENOENT 80*38768Smckusick * is returned. If the lookup fails, a status of zero is returned. 8137487Smckusick */ 8237487Smckusick cache_lookup(ndp) 8337487Smckusick register struct nameidata *ndp; 8437487Smckusick { 85*38768Smckusick register struct vnode *dvp; 8637487Smckusick register struct namecache *ncp; 8737487Smckusick union nchash *nhp; 8837487Smckusick 89*38768Smckusick if (!doingcache) 90*38768Smckusick return (0); 91*38768Smckusick if (ndp->ni_namelen > NCHNAMLEN) { 9237487Smckusick nchstats.ncs_long++; 9337487Smckusick ndp->ni_makeentry = 0; 9437487Smckusick return (0); 9537487Smckusick } 96*38768Smckusick dvp = ndp->ni_dvp; 97*38768Smckusick nhp = &nchash[NHASH(dvp, ndp->ni_hash)]; 9837487Smckusick for (ncp = nhp->nch_forw; ncp != (struct namecache *)nhp; 9937487Smckusick ncp = ncp->nc_forw) { 100*38768Smckusick if (ncp->nc_dvp == dvp && 101*38768Smckusick ncp->nc_dvpid == dvp->v_id && 102*38768Smckusick ncp->nc_nlen == ndp->ni_namelen && 103*38768Smckusick !bcmp(ncp->nc_name, ndp->ni_ptr, (unsigned)ncp->nc_nlen)) 10437487Smckusick break; 10537487Smckusick } 10637487Smckusick if (ncp == (struct namecache *)nhp) { 10737487Smckusick nchstats.ncs_miss++; 10837487Smckusick return (0); 10937487Smckusick } 110*38768Smckusick if (!ndp->ni_makeentry) { 111*38768Smckusick nchstats.ncs_badhits++; 112*38768Smckusick } else if (ncp->nc_vp == NULL) { 113*38768Smckusick nchstats.ncs_neghits++; 11437487Smckusick /* 115*38768Smckusick * move this slot to end of LRU chain, if not already there 11637487Smckusick */ 11737487Smckusick if (ncp->nc_nxt) { 11837487Smckusick /* remove from LRU chain */ 11937487Smckusick *ncp->nc_prev = ncp->nc_nxt; 12037487Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 12137487Smckusick /* and replace at end of it */ 12237487Smckusick ncp->nc_nxt = NULL; 12337487Smckusick ncp->nc_prev = nchtail; 12437487Smckusick *nchtail = ncp; 12537487Smckusick nchtail = &ncp->nc_nxt; 12637487Smckusick } 127*38768Smckusick return (ENOENT); 128*38768Smckusick } else if (ncp->nc_vpid != ncp->nc_vp->v_id) { 129*38768Smckusick nchstats.ncs_falsehits++; 130*38768Smckusick } else { 13137487Smckusick nchstats.ncs_goodhits++; 132*38768Smckusick /* 133*38768Smckusick * move this slot to end of LRU chain, if not already there 134*38768Smckusick */ 135*38768Smckusick if (ncp->nc_nxt) { 136*38768Smckusick /* remove from LRU chain */ 137*38768Smckusick *ncp->nc_prev = ncp->nc_nxt; 138*38768Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 139*38768Smckusick /* and replace at end of it */ 140*38768Smckusick ncp->nc_nxt = NULL; 141*38768Smckusick ncp->nc_prev = nchtail; 142*38768Smckusick *nchtail = ncp; 143*38768Smckusick nchtail = &ncp->nc_nxt; 144*38768Smckusick } 145*38768Smckusick ndp->ni_vp = ncp->nc_vp; 146*38768Smckusick return (-1); 14737487Smckusick } 14837487Smckusick 14937487Smckusick /* 15037487Smckusick * Last component and we are renaming or deleting, 15137487Smckusick * the cache entry is invalid, or otherwise don't 15237487Smckusick * want cache entry to exist. 15337487Smckusick */ 15437487Smckusick /* remove from LRU chain */ 15537487Smckusick *ncp->nc_prev = ncp->nc_nxt; 15637487Smckusick if (ncp->nc_nxt) 15737487Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 15837487Smckusick else 15937487Smckusick nchtail = ncp->nc_prev; 160*38768Smckusick /* remove from hash chain */ 161*38768Smckusick remque(ncp); 16237487Smckusick /* insert at head of LRU list (first to grab) */ 16337487Smckusick ncp->nc_nxt = nchhead; 16437487Smckusick ncp->nc_prev = &nchhead; 16537487Smckusick nchhead->nc_prev = &ncp->nc_nxt; 16637487Smckusick nchhead = ncp; 16737487Smckusick /* and make a dummy hash chain */ 16837487Smckusick ncp->nc_forw = ncp; 16937487Smckusick ncp->nc_back = ncp; 17037487Smckusick return (0); 17137487Smckusick } 17237487Smckusick 17337487Smckusick /* 17437487Smckusick * Add an entry to the cache 17537487Smckusick */ 17637487Smckusick cache_enter(ndp) 17737487Smckusick register struct nameidata *ndp; 17837487Smckusick { 17937487Smckusick register struct namecache *ncp; 18037487Smckusick union nchash *nhp; 18137487Smckusick 182*38768Smckusick if (!doingcache) 183*38768Smckusick return; 18437487Smckusick /* 18537487Smckusick * Free the cache slot at head of lru chain. 18637487Smckusick */ 18737487Smckusick if (ncp = nchhead) { 18837487Smckusick /* remove from lru chain */ 18937487Smckusick *ncp->nc_prev = ncp->nc_nxt; 19037487Smckusick if (ncp->nc_nxt) 19137487Smckusick ncp->nc_nxt->nc_prev = ncp->nc_prev; 19237487Smckusick else 19337487Smckusick nchtail = ncp->nc_prev; 194*38768Smckusick /* remove from old hash chain */ 195*38768Smckusick remque(ncp); 19637487Smckusick /* grab the inode we just found */ 19737487Smckusick ncp->nc_vp = ndp->ni_vp; 198*38768Smckusick if (ndp->ni_vp) 199*38768Smckusick ncp->nc_vpid = ndp->ni_vp->v_id; 200*38768Smckusick else 201*38768Smckusick ncp->nc_vpid = 0; 20237487Smckusick /* fill in cache info */ 203*38768Smckusick ncp->nc_dvp = ndp->ni_dvp; 204*38768Smckusick ncp->nc_dvpid = ndp->ni_dvp->v_id; 205*38768Smckusick ncp->nc_nlen = ndp->ni_namelen; 206*38768Smckusick bcopy(ndp->ni_ptr, ncp->nc_name, (unsigned)ncp->nc_nlen); 20737487Smckusick /* link at end of lru chain */ 20837487Smckusick ncp->nc_nxt = NULL; 20937487Smckusick ncp->nc_prev = nchtail; 21037487Smckusick *nchtail = ncp; 21137487Smckusick nchtail = &ncp->nc_nxt; 21237487Smckusick /* and insert on hash chain */ 213*38768Smckusick nhp = &nchash[NHASH(ndp->ni_vp, ndp->ni_hash)]; 21437487Smckusick insque(ncp, nhp); 21537487Smckusick } 21637487Smckusick } 21737487Smckusick 21837487Smckusick /* 21937487Smckusick * Name cache initialization, from main() when we are booting 22037487Smckusick */ 22137487Smckusick nchinit() 22237487Smckusick { 22337487Smckusick register union nchash *nchp; 22437487Smckusick register struct namecache *ncp; 22537487Smckusick 22637487Smckusick nchhead = 0; 22737487Smckusick nchtail = &nchhead; 22837487Smckusick for (ncp = namecache; ncp < &namecache[nchsize]; ncp++) { 22937487Smckusick ncp->nc_forw = ncp; /* hash chain */ 23037487Smckusick ncp->nc_back = ncp; 23137487Smckusick ncp->nc_nxt = NULL; /* lru chain */ 23237487Smckusick *nchtail = ncp; 23337487Smckusick ncp->nc_prev = nchtail; 23437487Smckusick nchtail = &ncp->nc_nxt; 23537487Smckusick /* all else is zero already */ 23637487Smckusick } 23737487Smckusick for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) { 23837487Smckusick nchp->nch_head[0] = nchp; 23937487Smckusick nchp->nch_head[1] = nchp; 24037487Smckusick } 24137487Smckusick } 24237487Smckusick 24337487Smckusick /* 24437487Smckusick * Cache flush, a particular vnode; called when a vnode is renamed to 245*38768Smckusick * hide entries that would now be invalid 24637487Smckusick */ 24737487Smckusick cache_purge(vp) 248*38768Smckusick struct vnode *vp; 24937487Smckusick { 250*38768Smckusick struct namecache *ncp; 25137487Smckusick 252*38768Smckusick vp->v_id = ++nextvnodeid; 253*38768Smckusick if (nextvnodeid != 0) 254*38768Smckusick return; 255*38768Smckusick for (ncp = namecache; ncp < &namecache[nchsize]; ncp++) { 256*38768Smckusick ncp->nc_vpid = 0; 257*38768Smckusick ncp->nc_dvpid = 0; 25837487Smckusick } 259*38768Smckusick vp->v_id = ++nextvnodeid; 26037487Smckusick } 26137487Smckusick 26237487Smckusick /* 26337487Smckusick * Cache flush, a whole filesystem; called when filesys is umounted to 26437487Smckusick * remove entries that would now be invalid 26537487Smckusick * 26637487Smckusick * The line "nxtcp = nchhead" near the end is to avoid potential problems 26737487Smckusick * if the cache lru chain is modified while we are dumping the 26837487Smckusick * inode. This makes the algorithm O(n^2), but do you think I care? 26937487Smckusick */ 27037487Smckusick cache_purgevfs(mp) 27137487Smckusick register struct mount *mp; 27237487Smckusick { 27337487Smckusick register struct namecache *ncp, *nxtcp; 27437487Smckusick 27537487Smckusick for (ncp = nchhead; ncp; ncp = nxtcp) { 27637487Smckusick nxtcp = ncp->nc_nxt; 277*38768Smckusick if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) 27837487Smckusick continue; 27937487Smckusick /* free the resources we had */ 28037487Smckusick ncp->nc_vp = NULL; 281*38768Smckusick ncp->nc_dvp = NULL; 28237487Smckusick remque(ncp); /* remove entry from its hash chain */ 28337487Smckusick ncp->nc_forw = ncp; /* and make a dummy one */ 28437487Smckusick ncp->nc_back = ncp; 28537487Smckusick /* delete this entry from LRU chain */ 28637487Smckusick *ncp->nc_prev = nxtcp; 28737487Smckusick if (nxtcp) 28837487Smckusick nxtcp->nc_prev = ncp->nc_prev; 28937487Smckusick else 29037487Smckusick nchtail = ncp->nc_prev; 29137487Smckusick /* cause rescan of list, it may have altered */ 29237487Smckusick nxtcp = nchhead; 29337487Smckusick /* put the now-free entry at head of LRU */ 29437487Smckusick ncp->nc_nxt = nxtcp; 29537487Smckusick ncp->nc_prev = &nchhead; 29637487Smckusick nxtcp->nc_prev = &ncp->nc_nxt; 29737487Smckusick nchhead = ncp; 29837487Smckusick } 29937487Smckusick } 300