vfs_cache.c revision 50477
11541Srgrimes/* 222521Sdyson * Copyright (c) 1989, 1993, 1995 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 522521Sdyson * This code is derived from software contributed to Berkeley by 622521Sdyson * Poul-Henning Kamp of the FreeBSD Project. 722521Sdyson * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 3. All advertising materials mentioning features or use of this software 171541Srgrimes * must display the following acknowledgement: 181541Srgrimes * This product includes software developed by the University of 191541Srgrimes * California, Berkeley and its contributors. 201541Srgrimes * 4. Neither the name of the University nor the names of its contributors 211541Srgrimes * may be used to endorse or promote products derived from this software 221541Srgrimes * without specific prior written permission. 231541Srgrimes * 241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341541Srgrimes * SUCH DAMAGE. 351541Srgrimes * 3623521Sbde * @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95 3750477Speter * $FreeBSD: head/sys/kern/vfs_cache.c 50477 1999-08-28 01:08:13Z peter $ 381541Srgrimes */ 391541Srgrimes 401541Srgrimes#include <sys/param.h> 411541Srgrimes#include <sys/systm.h> 4212820Sphk#include <sys/kernel.h> 4312820Sphk#include <sys/sysctl.h> 441541Srgrimes#include <sys/mount.h> 451541Srgrimes#include <sys/vnode.h> 461541Srgrimes#include <sys/namei.h> 471541Srgrimes#include <sys/malloc.h> 481541Srgrimes 4913490Sdyson 501541Srgrimes/* 511541Srgrimes * Name caching works as follows: 521541Srgrimes * 531541Srgrimes * Names found by directory scans are retained in a cache 541541Srgrimes * for future reference. It is managed LRU, so frequently 551541Srgrimes * used names will hang around. Cache is indexed by hash value 561541Srgrimes * obtained from (vp, name) where vp refers to the directory 571541Srgrimes * containing name. 581541Srgrimes * 5922521Sdyson * If it is a "negative" entry, (i.e. for a name that is known NOT to 6022521Sdyson * exist) the vnode pointer will be NULL. 616968Sphk * 621541Srgrimes * Upon reaching the last segment of a path, if the reference 631541Srgrimes * is for DELETE, or NOCACHE is set (rewrite), and the 641541Srgrimes * name is located in the cache, it will be dropped. 651541Srgrimes */ 661541Srgrimes 671541Srgrimes/* 681541Srgrimes * Structures associated with name cacheing. 691541Srgrimes */ 7022521Sdyson#define NCHHASH(dvp, cnp) \ 7129071Sphk (&nchashtbl[((dvp)->v_id + (cnp)->cn_hash) & nchash]) 7212820Sphkstatic LIST_HEAD(nchashhead, namecache) *nchashtbl; /* Hash Table */ 7325453Sphkstatic TAILQ_HEAD(, namecache) ncneg; /* Hash Table */ 7423521Sbdestatic u_long nchash; /* size of hash table */ 7529071SphkSYSCTL_INT(_debug, OID_AUTO, nchash, CTLFLAG_RD, &nchash, 0, ""); 7625453Sphkstatic u_long ncnegfactor = 16; /* ratio of negative entries */ 7725453SphkSYSCTL_INT(_debug, OID_AUTO, ncnegfactor, CTLFLAG_RW, &ncnegfactor, 0, ""); 7825453Sphkstatic u_long numneg; /* number of cache entries allocated */ 7925453SphkSYSCTL_INT(_debug, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0, ""); 8023521Sbdestatic u_long numcache; /* number of cache entries allocated */ 8125453SphkSYSCTL_INT(_debug, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0, ""); 8222521Sdysonstruct nchstats nchstats; /* cache effectiveness statistics */ 831541Srgrimes 8423521Sbdestatic int doingcache = 1; /* 1 => enable the cache */ 8523521SbdeSYSCTL_INT(_debug, OID_AUTO, vfscache, CTLFLAG_RW, &doingcache, 0, ""); 8625453SphkSYSCTL_INT(_debug, OID_AUTO, vnsize, CTLFLAG_RD, 0, sizeof(struct vnode), ""); 8725453SphkSYSCTL_INT(_debug, OID_AUTO, ncsize, CTLFLAG_RD, 0, sizeof(struct namecache), ""); 8823521Sbde 8929788Sphk/* 9029788Sphk * The new name cache statistics 9129788Sphk */ 9238984SbdeSYSCTL_NODE(_vfs, OID_AUTO, cache, CTLFLAG_RW, 0, "Name cache statistics"); 9329788Sphk#define STATNODE(mode, name, var) \ 9429788Sphk SYSCTL_INT(_vfs_cache, OID_AUTO, name, mode, var, 0, ""); 9529788SphkSTATNODE(CTLFLAG_RD, numneg, &numneg); 9629788SphkSTATNODE(CTLFLAG_RD, numcache, &numcache); 9729788Sphkstatic u_long numcalls; STATNODE(CTLFLAG_RD, numcalls, &numcalls); 9829788Sphkstatic u_long dothits; STATNODE(CTLFLAG_RD, dothits, &dothits); 9929788Sphkstatic u_long dotdothits; STATNODE(CTLFLAG_RD, dotdothits, &dotdothits); 10029788Sphkstatic u_long numchecks; STATNODE(CTLFLAG_RD, numchecks, &numchecks); 10129788Sphkstatic u_long nummiss; STATNODE(CTLFLAG_RD, nummiss, &nummiss); 10229804Sphkstatic u_long nummisszap; STATNODE(CTLFLAG_RD, nummisszap, &nummisszap); 10329788Sphkstatic u_long numposzaps; STATNODE(CTLFLAG_RD, numposzaps, &numposzaps); 10429788Sphkstatic u_long numposhits; STATNODE(CTLFLAG_RD, numposhits, &numposhits); 10529788Sphkstatic u_long numnegzaps; STATNODE(CTLFLAG_RD, numnegzaps, &numnegzaps); 10629788Sphkstatic u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits); 10729788Sphk 10829788Sphk 10925453Sphkstatic void cache_zap __P((struct namecache *ncp)); 1106968Sphk 11122521Sdyson/* 11225453Sphk * Flags in namecache.nc_flag 11325453Sphk */ 11425453Sphk#define NCF_WHITE 1 11525453Sphk/* 11622521Sdyson * Delete an entry from its hash list and move it to the front 11722521Sdyson * of the LRU list for immediate reuse. 11822521Sdyson */ 11925453Sphkstatic void 12025453Sphkcache_zap(ncp) 12125453Sphk struct namecache *ncp; 12225453Sphk{ 12325453Sphk LIST_REMOVE(ncp, nc_hash); 12425453Sphk LIST_REMOVE(ncp, nc_src); 12528954Sphk if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) 12628954Sphk vdrop(ncp->nc_dvp); 12725453Sphk if (ncp->nc_vp) { 12825453Sphk TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst); 12925453Sphk } else { 13025453Sphk TAILQ_REMOVE(&ncneg, ncp, nc_dst); 13125453Sphk numneg--; 13225453Sphk } 13325453Sphk numcache--; 13425453Sphk free(ncp, M_CACHE); 13522521Sdyson} 1366968Sphk 13722521Sdyson/* 13823521Sbde * Lookup an entry in the cache 1396968Sphk * 14023521Sbde * We don't do this if the segment name is long, simply so the cache 1416968Sphk * can avoid holding long names (which would either waste space, or 1421541Srgrimes * add greatly to the complexity). 1431541Srgrimes * 1446968Sphk * Lookup is called with dvp pointing to the directory to search, 14522521Sdyson * cnp pointing to the name of the entry being sought. If the lookup 14622521Sdyson * succeeds, the vnode is returned in *vpp, and a status of -1 is 14722521Sdyson * returned. If the lookup determines that the name does not exist 14822521Sdyson * (negative cacheing), a status of ENOENT is returned. If the lookup 14922521Sdyson * fails, a status of zero is returned. 1501541Srgrimes */ 1516968Sphk 1521541Srgrimesint 1531541Srgrimescache_lookup(dvp, vpp, cnp) 1541541Srgrimes struct vnode *dvp; 1551541Srgrimes struct vnode **vpp; 1561541Srgrimes struct componentname *cnp; 1571541Srgrimes{ 15831016Sphk register struct namecache *ncp; 1591541Srgrimes 1606928Sphk if (!doingcache) { 1616928Sphk cnp->cn_flags &= ~MAKEENTRY; 1621541Srgrimes return (0); 1636928Sphk } 16425453Sphk 16529788Sphk numcalls++; 16629788Sphk 16725453Sphk if (cnp->cn_nameptr[0] == '.') { 16825453Sphk if (cnp->cn_namelen == 1) { 16925453Sphk *vpp = dvp; 17029788Sphk dothits++; 17125453Sphk return (-1); 17225453Sphk } 17325453Sphk if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { 17429788Sphk dotdothits++; 17525453Sphk if (dvp->v_dd->v_id != dvp->v_ddid || 17625453Sphk (cnp->cn_flags & MAKEENTRY) == 0) { 17725453Sphk dvp->v_ddid = 0; 17825453Sphk return (0); 17925453Sphk } 18025453Sphk *vpp = dvp->v_dd; 18125453Sphk return (-1); 18225453Sphk } 1831541Srgrimes } 1846968Sphk 18525453Sphk LIST_FOREACH(ncp, (NCHHASH(dvp, cnp)), nc_hash) { 18629788Sphk numchecks++; 18725453Sphk if (ncp->nc_dvp == dvp && ncp->nc_nlen == cnp->cn_namelen && 18831879Sbde !bcmp(ncp->nc_name, cnp->cn_nameptr, ncp->nc_nlen)) 18922521Sdyson break; 1901541Srgrimes } 1916968Sphk 19222521Sdyson /* We failed to find an entry */ 19322521Sdyson if (ncp == 0) { 19429804Sphk if ((cnp->cn_flags & MAKEENTRY) == 0) { 19529804Sphk nummisszap++; 19629804Sphk } else { 19729804Sphk nummiss++; 19829804Sphk } 19922521Sdyson nchstats.ncs_miss++; 20022521Sdyson return (0); 20122521Sdyson } 20222521Sdyson 2036968Sphk /* We don't want to have an entry, so dump it */ 2046928Sphk if ((cnp->cn_flags & MAKEENTRY) == 0) { 20529788Sphk numposzaps++; 2061541Srgrimes nchstats.ncs_badhits++; 20725453Sphk cache_zap(ncp); 2086968Sphk return (0); 20923521Sbde } 2106968Sphk 2116968Sphk /* We found a "positive" match, return the vnode */ 21222521Sdyson if (ncp->nc_vp) { 21329788Sphk numposhits++; 2141541Srgrimes nchstats.ncs_goodhits++; 2151541Srgrimes *vpp = ncp->nc_vp; 2161541Srgrimes return (-1); 2171541Srgrimes } 2181541Srgrimes 2196968Sphk /* We found a negative match, and want to create it, so purge */ 2206968Sphk if (cnp->cn_nameiop == CREATE) { 22129788Sphk numnegzaps++; 2227013Sphk nchstats.ncs_badhits++; 22325453Sphk cache_zap(ncp); 2246968Sphk return (0); 2256968Sphk } 2266968Sphk 22729788Sphk numneghits++; 22822521Sdyson /* 22922521Sdyson * We found a "negative" match, ENOENT notifies client of this match. 23022521Sdyson * The nc_vpid field records whether this is a whiteout. 23122521Sdyson */ 23225453Sphk TAILQ_REMOVE(&ncneg, ncp, nc_dst); 23325453Sphk TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); 2346968Sphk nchstats.ncs_neghits++; 23525453Sphk if (ncp->nc_flag & NCF_WHITE) 23625453Sphk cnp->cn_flags |= ISWHITEOUT; 2376968Sphk return (ENOENT); 2381541Srgrimes} 2391541Srgrimes 2401541Srgrimes/* 2416968Sphk * Add an entry to the cache. 2421541Srgrimes */ 2431549Srgrimesvoid 2441541Srgrimescache_enter(dvp, vp, cnp) 2451541Srgrimes struct vnode *dvp; 2461541Srgrimes struct vnode *vp; 2471541Srgrimes struct componentname *cnp; 2481541Srgrimes{ 2496928Sphk register struct namecache *ncp; 2506928Sphk register struct nchashhead *ncpp; 2511541Srgrimes 2521541Srgrimes if (!doingcache) 2531541Srgrimes return; 2546968Sphk 25525453Sphk if (cnp->cn_nameptr[0] == '.') { 25625453Sphk if (cnp->cn_namelen == 1) { 25725453Sphk return; 2586928Sphk } 25925453Sphk if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { 26025453Sphk if (vp) { 26125453Sphk dvp->v_dd = vp; 26225453Sphk dvp->v_ddid = vp->v_id; 26325453Sphk } else { 26425453Sphk dvp->v_dd = dvp; 26525453Sphk dvp->v_ddid = 0; 26625453Sphk } 26725453Sphk return; 26825453Sphk } 2696968Sphk } 27025453Sphk 27125453Sphk ncp = (struct namecache *) 27225453Sphk malloc(sizeof *ncp + cnp->cn_namelen, M_CACHE, M_WAITOK); 27325453Sphk bzero((char *)ncp, sizeof *ncp); 27425453Sphk numcache++; 27528954Sphk if (!vp) { 27625453Sphk numneg++; 27728954Sphk ncp->nc_flag = cnp->cn_flags & ISWHITEOUT ? NCF_WHITE : 0; 27829071Sphk } else if (vp->v_type == VDIR) { 27929071Sphk vp->v_dd = dvp; 28029071Sphk vp->v_ddid = dvp->v_id; 28128954Sphk } 28223521Sbde 28322521Sdyson /* 28422521Sdyson * Fill in cache info, if vp is NULL this is a "negative" cache entry. 28522521Sdyson * For negative entries, we have to record whether it is a whiteout. 28622521Sdyson * the whiteout flag is stored in the nc_vpid field which is 28722521Sdyson * otherwise unused. 28822521Sdyson */ 2891541Srgrimes ncp->nc_vp = vp; 2901541Srgrimes ncp->nc_dvp = dvp; 2911541Srgrimes ncp->nc_nlen = cnp->cn_namelen; 29231879Sbde bcopy(cnp->cn_nameptr, ncp->nc_name, ncp->nc_nlen); 29322521Sdyson ncpp = NCHHASH(dvp, cnp); 2946928Sphk LIST_INSERT_HEAD(ncpp, ncp, nc_hash); 29528954Sphk if (LIST_EMPTY(&dvp->v_cache_src)) 29628954Sphk vhold(dvp); 29725453Sphk LIST_INSERT_HEAD(&dvp->v_cache_src, ncp, nc_src); 29825453Sphk if (vp) { 29925453Sphk TAILQ_INSERT_HEAD(&vp->v_cache_dst, ncp, nc_dst); 30025453Sphk } else { 30125453Sphk TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); 30225453Sphk } 30325453Sphk if (numneg*ncnegfactor > numcache) { 30425453Sphk ncp = TAILQ_FIRST(&ncneg); 30525453Sphk cache_zap(ncp); 30625453Sphk } 3071541Srgrimes} 3081541Srgrimes 3091541Srgrimes/* 3101541Srgrimes * Name cache initialization, from vfs_init() when we are booting 3111541Srgrimes */ 3121549Srgrimesvoid 3131541Srgrimesnchinit() 3141541Srgrimes{ 31523521Sbde 31625453Sphk TAILQ_INIT(&ncneg); 31729094Sphk nchashtbl = hashinit(desiredvnodes*2, M_CACHE, &nchash); 3181541Srgrimes} 3191541Srgrimes 3201541Srgrimes/* 32146011Sphk * Invalidate all entries to a particular vnode. 32223521Sbde * 32346011Sphk * Remove all entries in the namecache relating to this vnode and 32446011Sphk * change the v_id. We take the v_id from a global counter, since 32546011Sphk * it becomes a handy sequence number in crash-dumps that way. 32646011Sphk * No valid vnode will ever have (v_id == 0). 32746011Sphk * 32846011Sphk * XXX: Only time and the size of v_id prevents this from failing: 32946011Sphk * XXX: In theory we should hunt down all (struct vnode*, v_id) 33046011Sphk * XXX: soft references and nuke them, at least on the global 33146011Sphk * XXX: v_id wraparound. The period of resistance can be extended 33246011Sphk * XXX: by incrementing each vnodes v_id individually instead of 33346011Sphk * XXX: using the global v_id. 3341541Srgrimes */ 33546011Sphk 3361549Srgrimesvoid 3371541Srgrimescache_purge(vp) 3381541Srgrimes struct vnode *vp; 3391541Srgrimes{ 34029094Sphk static u_long nextid; 3411541Srgrimes 34225453Sphk while (!LIST_EMPTY(&vp->v_cache_src)) 34325453Sphk cache_zap(LIST_FIRST(&vp->v_cache_src)); 34425453Sphk while (!TAILQ_EMPTY(&vp->v_cache_dst)) 34525453Sphk cache_zap(TAILQ_FIRST(&vp->v_cache_dst)); 34625453Sphk 34746011Sphk do 34846011Sphk nextid++; 34946011Sphk while (nextid == vp->v_id || !nextid); 35029094Sphk vp->v_id = nextid; 35125453Sphk vp->v_dd = vp; 35225453Sphk vp->v_ddid = 0; 3531541Srgrimes} 3541541Srgrimes 3551541Srgrimes/* 3566968Sphk * Flush all entries referencing a particular filesystem. 3571541Srgrimes * 3586968Sphk * Since we need to check it anyway, we will flush all the invalid 35912968Sphk * entries at the same time. 3601541Srgrimes */ 3611549Srgrimesvoid 3621541Srgrimescache_purgevfs(mp) 3631541Srgrimes struct mount *mp; 3641541Srgrimes{ 3656968Sphk struct nchashhead *ncpp; 36622521Sdyson struct namecache *ncp, *nnp; 3671541Srgrimes 3686968Sphk /* Scan hash tables for applicable entries */ 36929071Sphk for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) { 37025453Sphk for (ncp = LIST_FIRST(ncpp); ncp != 0; ncp = nnp) { 37125453Sphk nnp = LIST_NEXT(ncp, nc_hash); 37225453Sphk if (ncp->nc_dvp->v_mount == mp) { 37325453Sphk cache_zap(ncp); 3746968Sphk } 3751541Srgrimes } 3761541Srgrimes } 3771541Srgrimes} 37828787Sphk 37928787Sphk/* 38028787Sphk * Perform canonical checks and cache lookup and pass on to filesystem 38128787Sphk * through the vop_cachedlookup only if needed. 38228787Sphk */ 38328787Sphk 38428787Sphkint 38528787Sphkvfs_cache_lookup(ap) 38628787Sphk struct vop_lookup_args /* { 38728787Sphk struct vnode *a_dvp; 38828787Sphk struct vnode **a_vpp; 38928787Sphk struct componentname *a_cnp; 39028787Sphk } */ *ap; 39128787Sphk{ 39228787Sphk struct vnode *vdp; 39328787Sphk struct vnode *pdp; 39428787Sphk int lockparent; 39528787Sphk int error; 39628787Sphk struct vnode **vpp = ap->a_vpp; 39728787Sphk struct componentname *cnp = ap->a_cnp; 39828787Sphk struct ucred *cred = cnp->cn_cred; 39928787Sphk int flags = cnp->cn_flags; 40028787Sphk struct proc *p = cnp->cn_proc; 40128787Sphk u_long vpid; /* capability number of vnode */ 40228787Sphk 40328787Sphk *vpp = NULL; 40428787Sphk vdp = ap->a_dvp; 40528787Sphk lockparent = flags & LOCKPARENT; 40628787Sphk 40728787Sphk if (vdp->v_type != VDIR) 40828787Sphk return (ENOTDIR); 40928787Sphk 41028787Sphk if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 41128787Sphk (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 41228787Sphk return (EROFS); 41328787Sphk 41428787Sphk error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc); 41528787Sphk 41628787Sphk if (error) 41728787Sphk return (error); 41828787Sphk 41928787Sphk error = cache_lookup(vdp, vpp, cnp); 42028787Sphk 42128787Sphk if (!error) 42230439Sphk return (VOP_CACHEDLOOKUP(ap->a_dvp, ap->a_vpp, ap->a_cnp)); 42328787Sphk 42428787Sphk if (error == ENOENT) 42528787Sphk return (error); 42628787Sphk 42728787Sphk pdp = vdp; 42828787Sphk vdp = *vpp; 42928787Sphk vpid = vdp->v_id; 43028787Sphk if (pdp == vdp) { /* lookup on "." */ 43128787Sphk VREF(vdp); 43228787Sphk error = 0; 43328787Sphk } else if (flags & ISDOTDOT) { 43428787Sphk VOP_UNLOCK(pdp, 0, p); 43528787Sphk error = vget(vdp, LK_EXCLUSIVE, p); 43628787Sphk if (!error && lockparent && (flags & ISLASTCN)) 43728787Sphk error = vn_lock(pdp, LK_EXCLUSIVE, p); 43828787Sphk } else { 43928787Sphk error = vget(vdp, LK_EXCLUSIVE, p); 44028787Sphk if (!lockparent || error || !(flags & ISLASTCN)) 44128787Sphk VOP_UNLOCK(pdp, 0, p); 44228787Sphk } 44328787Sphk /* 44428787Sphk * Check that the capability number did not change 44528787Sphk * while we were waiting for the lock. 44628787Sphk */ 44728787Sphk if (!error) { 44828787Sphk if (vpid == vdp->v_id) 44928787Sphk return (0); 45028787Sphk vput(vdp); 45128787Sphk if (lockparent && pdp != vdp && (flags & ISLASTCN)) 45228787Sphk VOP_UNLOCK(pdp, 0, p); 45328787Sphk } 45428787Sphk error = vn_lock(pdp, LK_EXCLUSIVE, p); 45528787Sphk if (error) 45628787Sphk return (error); 45730474Sphk return (VOP_CACHEDLOOKUP(ap->a_dvp, ap->a_vpp, ap->a_cnp)); 45828787Sphk} 459