vfs_cache.c revision 1.21 1 /* $NetBSD: vfs_cache.c,v 1.21 1999/09/10 23:24:23 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * @(#)vfs_cache.c 8.3 (Berkeley) 8/22/94
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/time.h>
41 #include <sys/mount.h>
42 #include <sys/vnode.h>
43 #include <sys/namei.h>
44 #include <sys/errno.h>
45 #include <sys/malloc.h>
46 #include <sys/pool.h>
47
48 /*
49 * Name caching works as follows:
50 *
51 * Names found by directory scans are retained in a cache
52 * for future reference. It is managed LRU, so frequently
53 * used names will hang around. Cache is indexed by hash value
54 * obtained from (dvp, name) where dvp refers to the directory
55 * containing name.
56 *
57 * For simplicity (and economy of storage), names longer than
58 * a maximum length of NCHNAMLEN are not cached; they occur
59 * infrequently in any case, and are almost never of interest.
60 *
61 * Upon reaching the last segment of a path, if the reference
62 * is for DELETE, or NOCACHE is set (rewrite), and the
63 * name is located in the cache, it will be dropped.
64 * The entry is dropped also when it was not possible to lock
65 * the cached vnode, either because vget() failed or the generation
66 * number has changed while waiting for the lock.
67 */
68
69 /*
70 * Structures associated with name cacheing.
71 */
72 LIST_HEAD(nchashhead, namecache) *nchashtbl;
73 u_long nchash; /* size of hash table - 1 */
74 long numcache; /* number of cache entries allocated */
75
76 LIST_HEAD(ncvhashhead, namecache) *ncvhashtbl;
77 u_long ncvhash; /* size of hash table - 1 */
78
79 TAILQ_HEAD(, namecache) nclruhead; /* LRU chain */
80 struct nchstats nchstats; /* cache effectiveness statistics */
81
82 struct pool namecache_pool;
83
84 int doingcache = 1; /* 1 => enable the cache */
85
86 /*
87 * Look for a the name in the cache. We don't do this
88 * if the segment name is long, simply so the cache can avoid
89 * holding long names (which would either waste space, or
90 * add greatly to the complexity).
91 *
92 * Lookup is called with ni_dvp pointing to the directory to search,
93 * ni_ptr pointing to the name of the entry being sought, ni_namelen
94 * tells the length of the name, and ni_hash contains a hash of
95 * the name. If the lookup succeeds, the vnode is locked, stored in ni_vp
96 * and a status of zero is returned. If the locking fails for whatever
97 * reason, the vnode is unlocked and the error is returned to caller.
98 * If the lookup determines that the name does not exist (negative cacheing),
99 * a status of ENOENT is returned. If the lookup fails, a status of -1
100 * is returned.
101 */
102 int
103 cache_lookup(dvp, vpp, cnp)
104 struct vnode *dvp;
105 struct vnode **vpp;
106 struct componentname *cnp;
107 {
108 register struct namecache *ncp;
109 register struct nchashhead *ncpp;
110 struct vnode *vp;
111 int vpid, error;
112
113 if (!doingcache) {
114 cnp->cn_flags &= ~MAKEENTRY;
115 *vpp = 0;
116 return (-1);
117 }
118 if (cnp->cn_namelen > NCHNAMLEN) {
119 nchstats.ncs_long++;
120 cnp->cn_flags &= ~MAKEENTRY;
121 *vpp = 0;
122 return (-1);
123 }
124 ncpp = &nchashtbl[(cnp->cn_hash ^ dvp->v_id) & nchash];
125 for (ncp = ncpp->lh_first; ncp != 0; ncp = ncp->nc_hash.le_next) {
126 if (ncp->nc_dvp == dvp &&
127 ncp->nc_dvpid == dvp->v_id &&
128 ncp->nc_nlen == cnp->cn_namelen &&
129 !memcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
130 break;
131 }
132 if (ncp == 0) {
133 nchstats.ncs_miss++;
134 *vpp = 0;
135 return (-1);
136 }
137 if ((cnp->cn_flags & MAKEENTRY) == 0) {
138 nchstats.ncs_badhits++;
139 goto remove;
140 } else if (ncp->nc_vp == NULL) {
141 /*
142 * Restore the ISWHITEOUT flag saved earlier.
143 */
144 cnp->cn_flags |= ncp->nc_vpid;
145 if (cnp->cn_nameiop != CREATE ||
146 (cnp->cn_flags & ISLASTCN) == 0) {
147 nchstats.ncs_neghits++;
148 /*
149 * Move this slot to end of LRU chain,
150 * if not already there.
151 */
152 if (ncp->nc_lru.tqe_next != 0) {
153 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
154 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
155 }
156 return (ENOENT);
157 } else {
158 nchstats.ncs_badhits++;
159 goto remove;
160 }
161 } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
162 nchstats.ncs_falsehits++;
163 goto remove;
164 }
165
166 vp = ncp->nc_vp;
167 vpid = vp->v_id;
168 if (vp == dvp) { /* lookup on "." */
169 VREF(dvp);
170 error = 0;
171 } else if (cnp->cn_flags & ISDOTDOT) {
172 VOP_UNLOCK(dvp, 0);
173 cnp->cn_flags |= PDIRUNLOCK;
174 error = vget(vp, LK_EXCLUSIVE);
175 /* if the above vget() succeeded and both LOCKPARENT and
176 * ISLASTCN is set, lock the directory vnode as well */
177 if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
178 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0) {
179 vput(vp);
180 return (error);
181 }
182 cnp->cn_flags &= ~PDIRUNLOCK;
183 }
184 } else {
185 error = vget(vp, LK_EXCLUSIVE);
186 /* if the above vget() failed or either of LOCKPARENT or
187 * ISLASTCN is set, unlock the directory vnode */
188 if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
189 VOP_UNLOCK(dvp, 0);
190 cnp->cn_flags |= PDIRUNLOCK;
191 }
192 }
193
194 /*
195 * Check that the lock succeeded, and that the capability number did
196 * not change while we were waiting for the lock.
197 */
198 if (error || vpid != vp->v_id) {
199 if (!error) {
200 vput(vp);
201 nchstats.ncs_falsehits++;
202 } else
203 nchstats.ncs_badhits++;
204 /*
205 * The parent needs to be locked when we return to VOP_LOOKUP().
206 * The `.' case here should be extremely rare (if it can happen
207 * at all), so we don't bother optimizing out the unlock/relock.
208 */
209 if (vp == dvp ||
210 error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
211 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0)
212 return (error);
213 cnp->cn_flags &= ~PDIRUNLOCK;
214 }
215 goto remove;
216 }
217
218 nchstats.ncs_goodhits++;
219 /*
220 * move this slot to end of LRU chain, if not already there
221 */
222 if (ncp->nc_lru.tqe_next != 0) {
223 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
224 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
225 }
226 *vpp = vp;
227 return (0);
228
229 remove:
230 /*
231 * Last component and we are renaming or deleting,
232 * the cache entry is invalid, or otherwise don't
233 * want cache entry to exist.
234 */
235 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
236 LIST_REMOVE(ncp, nc_hash);
237 ncp->nc_hash.le_prev = 0;
238 if (ncp->nc_vhash.le_prev != NULL) {
239 LIST_REMOVE(ncp, nc_vhash);
240 ncp->nc_vhash.le_prev = 0;
241 }
242 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
243 *vpp = 0;
244 return (-1);
245 }
246
247 /*
248 * Scan cache looking for name of directory entry pointing at vp.
249 *
250 * Fill in dvpp.
251 *
252 * If bufp is non-NULL, also place the name in the buffer which starts
253 * at bufp, immediately before *bpp, and move bpp backwards to point
254 * at the start of it. (Yes, this is a little baroque, but it's done
255 * this way to cater to the whims of getcwd).
256 *
257 * Returns 0 on success, -1 on cache miss, positive errno on failure.
258 */
259 int
260 cache_revlookup (vp, dvpp, bpp, bufp)
261 struct vnode *vp, **dvpp;
262 char **bpp;
263 char *bufp;
264 {
265 struct namecache *ncp;
266 struct vnode *dvp;
267 struct ncvhashhead *nvcpp;
268
269 if (!doingcache)
270 goto out;
271
272 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
273
274 for (ncp = nvcpp->lh_first; ncp != 0; ncp = ncp->nc_vhash.le_next) {
275 if ((ncp->nc_vp == vp) &&
276 (ncp->nc_vpid == vp->v_id) &&
277 ((dvp = ncp->nc_dvp) != 0) &&
278 (dvp != vp) && /* avoid pesky . entries.. */
279 (dvp->v_id == ncp->nc_dvpid))
280 {
281 char *bp;
282
283 #ifdef DIAGNOSTIC
284 if ((ncp->nc_nlen == 1) &&
285 (ncp->nc_name[0] == '.'))
286 panic("cache_revlookup: found entry for .");
287
288 if ((ncp->nc_nlen == 2) &&
289 (ncp->nc_name[0] == '.') &&
290 (ncp->nc_name[1] == '.'))
291 panic("cache_revlookup: found entry for ..");
292 #endif
293 nchstats.ncs_revhits++;
294
295 if (bufp) {
296 bp = *bpp;
297 bp -= ncp->nc_nlen;
298 if (bp <= bufp) {
299 *dvpp = 0;
300 return ERANGE;
301 }
302 memcpy(bp, ncp->nc_name, ncp->nc_nlen);
303 *bpp = bp;
304 }
305
306 /* XXX MP: how do we know dvp won't evaporate? */
307 *dvpp = dvp;
308 return 0;
309 }
310 }
311 nchstats.ncs_revmiss++;
312 out:
313 *dvpp = 0;
314 return -1;
315 }
316
317 /*
318 * Add an entry to the cache
319 */
320 void
321 cache_enter(dvp, vp, cnp)
322 struct vnode *dvp;
323 struct vnode *vp;
324 struct componentname *cnp;
325 {
326 register struct namecache *ncp;
327 register struct nchashhead *ncpp;
328 register struct ncvhashhead *nvcpp;
329
330 #ifdef DIAGNOSTIC
331 if (cnp->cn_namelen > NCHNAMLEN)
332 panic("cache_enter: name too long");
333 #endif
334 if (!doingcache)
335 return;
336 /*
337 * Free the cache slot at head of lru chain.
338 */
339 if (numcache < desiredvnodes) {
340 ncp = pool_get(&namecache_pool, PR_WAITOK);
341 memset((char *)ncp, 0, sizeof(*ncp));
342 numcache++;
343 } else if ((ncp = nclruhead.tqh_first) != NULL) {
344 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
345 if (ncp->nc_hash.le_prev != 0) {
346 LIST_REMOVE(ncp, nc_hash);
347 ncp->nc_hash.le_prev = 0;
348 }
349 if (ncp->nc_vhash.le_prev != 0) {
350 LIST_REMOVE(ncp, nc_vhash);
351 ncp->nc_vhash.le_prev = 0;
352 }
353 } else
354 return;
355 /* grab the vnode we just found */
356 ncp->nc_vp = vp;
357 if (vp)
358 ncp->nc_vpid = vp->v_id;
359 else {
360 /*
361 * For negative hits, save the ISWHITEOUT flag so we can
362 * restore it later when the cache entry is used again.
363 */
364 ncp->nc_vpid = cnp->cn_flags & ISWHITEOUT;
365 }
366 /* fill in cache info */
367 ncp->nc_dvp = dvp;
368 ncp->nc_dvpid = dvp->v_id;
369 ncp->nc_nlen = cnp->cn_namelen;
370 memcpy(ncp->nc_name, cnp->cn_nameptr, (unsigned)ncp->nc_nlen);
371 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
372 ncpp = &nchashtbl[(cnp->cn_hash ^ dvp->v_id) & nchash];
373 LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
374
375 ncp->nc_vhash.le_prev = 0;
376 ncp->nc_vhash.le_next = 0;
377
378 /*
379 * Create reverse-cache entries (used in getcwd) for directories.
380 */
381 if (vp &&
382 (vp != dvp) &&
383 (vp->v_type == VDIR) &&
384 ((ncp->nc_nlen > 2) ||
385 ((ncp->nc_nlen == 2) && (ncp->nc_name[0] != '.') && (ncp->nc_name[1] != '.')) ||
386 ((ncp->nc_nlen == 1) && (ncp->nc_name[0] != '.'))))
387 {
388 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
389 LIST_INSERT_HEAD(nvcpp, ncp, nc_vhash);
390 }
391
392 }
393
394 /*
395 * Name cache initialization, from vfs_init() when we are booting
396 */
397 void
398 nchinit()
399 {
400
401 TAILQ_INIT(&nclruhead);
402 nchashtbl = hashinit(desiredvnodes, M_CACHE, M_WAITOK, &nchash);
403 ncvhashtbl = hashinit(desiredvnodes/8, M_CACHE, M_WAITOK, &ncvhash);
404 pool_init(&namecache_pool, sizeof(struct namecache), 0, 0, 0,
405 "ncachepl", 0, pool_page_alloc_nointr, pool_page_free_nointr,
406 M_CACHE);
407 }
408
409 /*
410 * Cache flush, a particular vnode; called when a vnode is renamed to
411 * hide entries that would now be invalid
412 */
413 void
414 cache_purge(vp)
415 struct vnode *vp;
416 {
417 struct namecache *ncp;
418 struct nchashhead *ncpp;
419
420 vp->v_id = ++nextvnodeid;
421 if (nextvnodeid != 0)
422 return;
423 for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
424 for (ncp = ncpp->lh_first; ncp != 0; ncp = ncp->nc_hash.le_next) {
425 ncp->nc_vpid = 0;
426 ncp->nc_dvpid = 0;
427 }
428 }
429 vp->v_id = ++nextvnodeid;
430 }
431
432 /*
433 * Cache flush, a whole filesystem; called when filesys is umounted to
434 * remove entries that would now be invalid
435 *
436 * The line "nxtcp = nchhead" near the end is to avoid potential problems
437 * if the cache lru chain is modified while we are dumping the
438 * inode. This makes the algorithm O(n^2), but do you think I care?
439 */
440 void
441 cache_purgevfs(mp)
442 struct mount *mp;
443 {
444 register struct namecache *ncp, *nxtcp;
445
446 for (ncp = nclruhead.tqh_first; ncp != 0; ncp = nxtcp) {
447 if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) {
448 nxtcp = ncp->nc_lru.tqe_next;
449 continue;
450 }
451 /* free the resources we had */
452 ncp->nc_vp = NULL;
453 ncp->nc_dvp = NULL;
454 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
455 if (ncp->nc_hash.le_prev != 0) {
456 LIST_REMOVE(ncp, nc_hash);
457 ncp->nc_hash.le_prev = 0;
458 }
459 if (ncp->nc_vhash.le_prev != 0) {
460 LIST_REMOVE(ncp, nc_vhash);
461 ncp->nc_vhash.le_prev = 0;
462 }
463 /* cause rescan of list, it may have altered */
464 nxtcp = nclruhead.tqh_first;
465 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
466 }
467 }
468
469