vfs_cache.c revision 1.28 1 /* $NetBSD: vfs_cache.c,v 1.28 2000/11/24 07:25:51 chs 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 "opt_ddb.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/time.h>
43 #include <sys/mount.h>
44 #include <sys/vnode.h>
45 #include <sys/namei.h>
46 #include <sys/errno.h>
47 #include <sys/malloc.h>
48 #include <sys/pool.h>
49
50 /*
51 * Name caching works as follows:
52 *
53 * Names found by directory scans are retained in a cache
54 * for future reference. It is managed LRU, so frequently
55 * used names will hang around. Cache is indexed by hash value
56 * obtained from (dvp, name) where dvp refers to the directory
57 * containing name.
58 *
59 * For simplicity (and economy of storage), names longer than
60 * a maximum length of NCHNAMLEN are not cached; they occur
61 * infrequently in any case, and are almost never of interest.
62 *
63 * Upon reaching the last segment of a path, if the reference
64 * is for DELETE, or NOCACHE is set (rewrite), and the
65 * name is located in the cache, it will be dropped.
66 * The entry is dropped also when it was not possible to lock
67 * the cached vnode, either because vget() failed or the generation
68 * number has changed while waiting for the lock.
69 */
70
71 /*
72 * Structures associated with name cacheing.
73 */
74 LIST_HEAD(nchashhead, namecache) *nchashtbl;
75 u_long nchash; /* size of hash table - 1 */
76 long numcache; /* number of cache entries allocated */
77
78 LIST_HEAD(ncvhashhead, namecache) *ncvhashtbl;
79 u_long ncvhash; /* size of hash table - 1 */
80
81 TAILQ_HEAD(, namecache) nclruhead; /* LRU chain */
82 struct nchstats nchstats; /* cache effectiveness statistics */
83
84 struct pool namecache_pool;
85
86 int doingcache = 1; /* 1 => enable the cache */
87
88 /*
89 * Look for a the name in the cache. We don't do this
90 * if the segment name is long, simply so the cache can avoid
91 * holding long names (which would either waste space, or
92 * add greatly to the complexity).
93 *
94 * Lookup is called with ni_dvp pointing to the directory to search,
95 * ni_ptr pointing to the name of the entry being sought, ni_namelen
96 * tells the length of the name, and ni_hash contains a hash of
97 * the name. If the lookup succeeds, the vnode is locked, stored in ni_vp
98 * and a status of zero is returned. If the locking fails for whatever
99 * reason, the vnode is unlocked and the error is returned to caller.
100 * If the lookup determines that the name does not exist (negative cacheing),
101 * a status of ENOENT is returned. If the lookup fails, a status of -1
102 * is returned.
103 */
104 int
105 cache_lookup(dvp, vpp, cnp)
106 struct vnode *dvp;
107 struct vnode **vpp;
108 struct componentname *cnp;
109 {
110 struct namecache *ncp;
111 struct nchashhead *ncpp;
112 struct vnode *vp;
113 int vpid, error;
114
115 if (!doingcache) {
116 cnp->cn_flags &= ~MAKEENTRY;
117 *vpp = 0;
118 return (-1);
119 }
120 if (cnp->cn_namelen > NCHNAMLEN) {
121 nchstats.ncs_long++;
122 cnp->cn_flags &= ~MAKEENTRY;
123 *vpp = 0;
124 return (-1);
125 }
126 ncpp = &nchashtbl[(cnp->cn_hash ^ dvp->v_id) & nchash];
127 LIST_FOREACH(ncp, ncpp, nc_hash) {
128 if (ncp->nc_dvp == dvp &&
129 ncp->nc_dvpid == dvp->v_id &&
130 ncp->nc_nlen == cnp->cn_namelen &&
131 !memcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
132 break;
133 }
134 if (ncp == 0) {
135 nchstats.ncs_miss++;
136 *vpp = 0;
137 return (-1);
138 }
139 if ((cnp->cn_flags & MAKEENTRY) == 0) {
140 nchstats.ncs_badhits++;
141 goto remove;
142 } else if (ncp->nc_vp == NULL) {
143 /*
144 * Restore the ISWHITEOUT flag saved earlier.
145 */
146 cnp->cn_flags |= ncp->nc_vpid;
147 if (cnp->cn_nameiop != CREATE ||
148 (cnp->cn_flags & ISLASTCN) == 0) {
149 nchstats.ncs_neghits++;
150 /*
151 * Move this slot to end of LRU chain,
152 * if not already there.
153 */
154 if (ncp->nc_lru.tqe_next != 0) {
155 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
156 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
157 }
158 return (ENOENT);
159 } else {
160 nchstats.ncs_badhits++;
161 goto remove;
162 }
163 } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
164 nchstats.ncs_falsehits++;
165 goto remove;
166 }
167
168 vp = ncp->nc_vp;
169 vpid = vp->v_id;
170 if (vp == dvp) { /* lookup on "." */
171 VREF(dvp);
172 error = 0;
173 } else if (cnp->cn_flags & ISDOTDOT) {
174 VOP_UNLOCK(dvp, 0);
175 cnp->cn_flags |= PDIRUNLOCK;
176 error = vget(vp, LK_EXCLUSIVE);
177 /* if the above vget() succeeded and both LOCKPARENT and
178 * ISLASTCN is set, lock the directory vnode as well */
179 if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
180 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0) {
181 vput(vp);
182 return (error);
183 }
184 cnp->cn_flags &= ~PDIRUNLOCK;
185 }
186 } else {
187 error = vget(vp, LK_EXCLUSIVE);
188 /* if the above vget() failed or either of LOCKPARENT or
189 * ISLASTCN is set, unlock the directory vnode */
190 if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
191 VOP_UNLOCK(dvp, 0);
192 cnp->cn_flags |= PDIRUNLOCK;
193 }
194 }
195
196 /*
197 * Check that the lock succeeded, and that the capability number did
198 * not change while we were waiting for the lock.
199 */
200 if (error || vpid != vp->v_id) {
201 if (!error) {
202 vput(vp);
203 nchstats.ncs_falsehits++;
204 } else
205 nchstats.ncs_badhits++;
206 /*
207 * The parent needs to be locked when we return to VOP_LOOKUP().
208 * The `.' case here should be extremely rare (if it can happen
209 * at all), so we don't bother optimizing out the unlock/relock.
210 */
211 if (vp == dvp ||
212 error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
213 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0)
214 return (error);
215 cnp->cn_flags &= ~PDIRUNLOCK;
216 }
217 goto remove;
218 }
219
220 nchstats.ncs_goodhits++;
221 /*
222 * move this slot to end of LRU chain, if not already there
223 */
224 if (ncp->nc_lru.tqe_next != 0) {
225 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
226 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
227 }
228 *vpp = vp;
229 return (0);
230
231 remove:
232 /*
233 * Last component and we are renaming or deleting,
234 * the cache entry is invalid, or otherwise don't
235 * want cache entry to exist.
236 */
237 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
238 LIST_REMOVE(ncp, nc_hash);
239 ncp->nc_hash.le_prev = 0;
240 if (ncp->nc_vhash.le_prev != NULL) {
241 LIST_REMOVE(ncp, nc_vhash);
242 ncp->nc_vhash.le_prev = 0;
243 }
244 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
245 *vpp = 0;
246 return (-1);
247 }
248
249 /*
250 * Scan cache looking for name of directory entry pointing at vp.
251 *
252 * Fill in dvpp.
253 *
254 * If bufp is non-NULL, also place the name in the buffer which starts
255 * at bufp, immediately before *bpp, and move bpp backwards to point
256 * at the start of it. (Yes, this is a little baroque, but it's done
257 * this way to cater to the whims of getcwd).
258 *
259 * Returns 0 on success, -1 on cache miss, positive errno on failure.
260 */
261 int
262 cache_revlookup (vp, dvpp, bpp, bufp)
263 struct vnode *vp, **dvpp;
264 char **bpp;
265 char *bufp;
266 {
267 struct namecache *ncp;
268 struct vnode *dvp;
269 struct ncvhashhead *nvcpp;
270
271 if (!doingcache)
272 goto out;
273
274 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
275
276 LIST_FOREACH(ncp, nvcpp, nc_vhash) {
277 if ((ncp->nc_vp == vp) &&
278 (ncp->nc_vpid == vp->v_id) &&
279 ((dvp = ncp->nc_dvp) != 0) &&
280 (dvp != vp) && /* avoid pesky . entries.. */
281 (dvp->v_id == ncp->nc_dvpid))
282 {
283 char *bp;
284
285 #ifdef DIAGNOSTIC
286 if ((ncp->nc_nlen == 1) &&
287 (ncp->nc_name[0] == '.'))
288 panic("cache_revlookup: found entry for .");
289
290 if ((ncp->nc_nlen == 2) &&
291 (ncp->nc_name[0] == '.') &&
292 (ncp->nc_name[1] == '.'))
293 panic("cache_revlookup: found entry for ..");
294 #endif
295 nchstats.ncs_revhits++;
296
297 if (bufp) {
298 bp = *bpp;
299 bp -= ncp->nc_nlen;
300 if (bp <= bufp) {
301 *dvpp = 0;
302 return ERANGE;
303 }
304 memcpy(bp, ncp->nc_name, ncp->nc_nlen);
305 *bpp = bp;
306 }
307
308 /* XXX MP: how do we know dvp won't evaporate? */
309 *dvpp = dvp;
310 return 0;
311 }
312 }
313 nchstats.ncs_revmiss++;
314 out:
315 *dvpp = 0;
316 return -1;
317 }
318
319 /*
320 * Add an entry to the cache
321 */
322 void
323 cache_enter(dvp, vp, cnp)
324 struct vnode *dvp;
325 struct vnode *vp;
326 struct componentname *cnp;
327 {
328 struct namecache *ncp;
329 struct nchashhead *ncpp;
330 struct ncvhashhead *nvcpp;
331
332 #ifdef DIAGNOSTIC
333 if (cnp->cn_namelen > NCHNAMLEN)
334 panic("cache_enter: name too long");
335 #endif
336 if (!doingcache)
337 return;
338 /*
339 * Free the cache slot at head of lru chain.
340 */
341 if (numcache < numvnodes) {
342 ncp = pool_get(&namecache_pool, PR_WAITOK);
343 memset(ncp, 0, sizeof(*ncp));
344 numcache++;
345 } else if ((ncp = TAILQ_FIRST(&nclruhead)) != NULL) {
346 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
347 if (ncp->nc_hash.le_prev != 0) {
348 LIST_REMOVE(ncp, nc_hash);
349 ncp->nc_hash.le_prev = 0;
350 }
351 if (ncp->nc_vhash.le_prev != 0) {
352 LIST_REMOVE(ncp, nc_vhash);
353 ncp->nc_vhash.le_prev = 0;
354 }
355 } else
356 return;
357 /* grab the vnode we just found */
358 ncp->nc_vp = vp;
359 if (vp)
360 ncp->nc_vpid = vp->v_id;
361 else {
362 /*
363 * For negative hits, save the ISWHITEOUT flag so we can
364 * restore it later when the cache entry is used again.
365 */
366 ncp->nc_vpid = cnp->cn_flags & ISWHITEOUT;
367 }
368 /* fill in cache info */
369 ncp->nc_dvp = dvp;
370 ncp->nc_dvpid = dvp->v_id;
371 ncp->nc_nlen = cnp->cn_namelen;
372 memcpy(ncp->nc_name, cnp->cn_nameptr, (unsigned)ncp->nc_nlen);
373 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
374 ncpp = &nchashtbl[(cnp->cn_hash ^ dvp->v_id) & nchash];
375 LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
376
377 ncp->nc_vhash.le_prev = 0;
378 ncp->nc_vhash.le_next = 0;
379
380 /*
381 * Create reverse-cache entries (used in getcwd) for directories.
382 */
383 if (vp &&
384 (vp != dvp) &&
385 (vp->v_type == VDIR) &&
386 ((ncp->nc_nlen > 2) ||
387 ((ncp->nc_nlen == 2) && (ncp->nc_name[0] != '.') && (ncp->nc_name[1] != '.')) ||
388 ((ncp->nc_nlen == 1) && (ncp->nc_name[0] != '.'))))
389 {
390 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
391 LIST_INSERT_HEAD(nvcpp, ncp, nc_vhash);
392 }
393
394 }
395
396 /*
397 * Name cache initialization, from vfs_init() when we are booting
398 */
399 void
400 nchinit()
401 {
402
403 TAILQ_INIT(&nclruhead);
404 nchashtbl =
405 hashinit(desiredvnodes, HASH_LIST, M_CACHE, M_WAITOK, &nchash);
406 ncvhashtbl =
407 hashinit(desiredvnodes/8, HASH_LIST, M_CACHE, M_WAITOK, &ncvhash);
408 pool_init(&namecache_pool, sizeof(struct namecache), 0, 0, 0,
409 "ncachepl", 0, pool_page_alloc_nointr, pool_page_free_nointr,
410 M_CACHE);
411 }
412
413 /*
414 * Cache flush, a particular vnode; called when a vnode is renamed to
415 * hide entries that would now be invalid
416 */
417 void
418 cache_purge(vp)
419 struct vnode *vp;
420 {
421 struct namecache *ncp;
422 struct nchashhead *ncpp;
423 static u_long nextvnodeid;
424
425 vp->v_id = ++nextvnodeid;
426 if (nextvnodeid != 0)
427 return;
428 for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
429 LIST_FOREACH(ncp, ncpp, nc_hash) {
430 ncp->nc_vpid = 0;
431 ncp->nc_dvpid = 0;
432 }
433 }
434 vp->v_id = ++nextvnodeid;
435 }
436
437 /*
438 * Cache flush, a whole filesystem; called when filesys is umounted to
439 * remove entries that would now be invalid.
440 */
441 void
442 cache_purgevfs(mp)
443 struct mount *mp;
444 {
445 struct namecache *ncp, *nxtcp;
446
447 for (ncp = TAILQ_FIRST(&nclruhead); ncp != NULL; ncp = nxtcp) {
448 nxtcp = TAILQ_NEXT(ncp, nc_lru);
449 if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) {
450 continue;
451 }
452 /* free the resources we had */
453 ncp->nc_vp = NULL;
454 ncp->nc_dvp = NULL;
455 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
456 if (ncp->nc_hash.le_prev != 0) {
457 LIST_REMOVE(ncp, nc_hash);
458 ncp->nc_hash.le_prev = 0;
459 }
460 if (ncp->nc_vhash.le_prev != 0) {
461 LIST_REMOVE(ncp, nc_vhash);
462 ncp->nc_vhash.le_prev = 0;
463 }
464 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
465 }
466 }
467
468 #ifdef DDB
469 void
470 namecache_print(struct vnode *vp, void (*pr)(const char *, ...))
471 {
472 struct vnode *dvp = NULL;
473 struct namecache *ncp;
474
475 TAILQ_FOREACH(ncp, &nclruhead, nc_lru) {
476 if (ncp->nc_vp == vp && ncp->nc_vpid == vp->v_id) {
477 (*pr)("name %.*s\n", ncp->nc_nlen, ncp->nc_name);
478 dvp = ncp->nc_dvp;
479 }
480 }
481 if (dvp == NULL) {
482 (*pr)("name not found\n");
483 return;
484 }
485 vp = dvp;
486 TAILQ_FOREACH(ncp, &nclruhead, nc_lru) {
487 if (ncp->nc_vp == vp && ncp->nc_vpid == vp->v_id) {
488 (*pr)("parent %.*s\n", ncp->nc_nlen, ncp->nc_name);
489 }
490 }
491 }
492 #endif
493