vfs_cache.c revision 1.20 1 /* $NetBSD: vfs_cache.c,v 1.20 1999/09/05 14:22:34 jdolecek 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 nchstats.ncs_neghits++;
147 /*
148 * Move this slot to end of LRU chain,
149 * if not already there.
150 */
151 if (ncp->nc_lru.tqe_next != 0) {
152 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
153 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
154 }
155 return (ENOENT);
156 } else {
157 nchstats.ncs_badhits++;
158 goto remove;
159 }
160 } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
161 nchstats.ncs_falsehits++;
162 goto remove;
163 }
164
165 vp = ncp->nc_vp;
166 vpid = vp->v_id;
167 if (vp == dvp) { /* lookup on "." */
168 VREF(dvp);
169 error = 0;
170 } else if (cnp->cn_flags & ISDOTDOT) {
171 VOP_UNLOCK(dvp, 0);
172 cnp->cn_flags |= PDIRUNLOCK;
173 error = vget(vp, LK_EXCLUSIVE);
174 /* if the above vget() succeeded and both LOCKPARENT and
175 * ISLASTCN is set, lock the directory vnode as well */
176 if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
177 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0) {
178 vput(vp);
179 return (error);
180 }
181 cnp->cn_flags &= ~PDIRUNLOCK;
182 }
183 } else {
184 error = vget(vp, LK_EXCLUSIVE);
185 /* if the above vget() failed or either of LOCKPARENT or
186 * ISLASTCN is set, unlock the directory vnode */
187 if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
188 VOP_UNLOCK(dvp, 0);
189 cnp->cn_flags |= PDIRUNLOCK;
190 }
191 }
192
193 /*
194 * Check that the lock succeeded, and that the capability number did
195 * not change while we were waiting for the lock.
196 */
197 if (error || vpid != vp->v_id) {
198 if (!error) {
199 vput(vp);
200 nchstats.ncs_falsehits++;
201 } else
202 nchstats.ncs_badhits++;
203 /*
204 * The parent needs to be locked when we return to VOP_LOOKUP().
205 * The `.' case here should be extremely rare (if it can happen
206 * at all), so we don't bother optimizing out the unlock/relock.
207 */
208 if (vp == dvp ||
209 error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
210 if ((error = vn_lock(dvp, LK_EXCLUSIVE)) != 0)
211 return (error);
212 cnp->cn_flags &= ~PDIRUNLOCK;
213 }
214 goto remove;
215 }
216
217 nchstats.ncs_goodhits++;
218 /*
219 * move this slot to end of LRU chain, if not already there
220 */
221 if (ncp->nc_lru.tqe_next != 0) {
222 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
223 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
224 }
225 *vpp = vp;
226 return (0);
227
228 remove:
229 /*
230 * Last component and we are renaming or deleting,
231 * the cache entry is invalid, or otherwise don't
232 * want cache entry to exist.
233 */
234 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
235 LIST_REMOVE(ncp, nc_hash);
236 ncp->nc_hash.le_prev = 0;
237 if (ncp->nc_vhash.le_prev != NULL) {
238 LIST_REMOVE(ncp, nc_vhash);
239 ncp->nc_vhash.le_prev = 0;
240 }
241 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
242 *vpp = 0;
243 return (-1);
244 }
245
246 /*
247 * Scan cache looking for name of directory entry pointing at vp.
248 *
249 * Fill in dvpp.
250 *
251 * If bufp is non-NULL, also place the name in the buffer which starts
252 * at bufp, immediately before *bpp, and move bpp backwards to point
253 * at the start of it. (Yes, this is a little baroque, but it's done
254 * this way to cater to the whims of getcwd).
255 *
256 * Returns 0 on success, -1 on cache miss, positive errno on failure.
257 */
258 int
259 cache_revlookup (vp, dvpp, bpp, bufp)
260 struct vnode *vp, **dvpp;
261 char **bpp;
262 char *bufp;
263 {
264 struct namecache *ncp;
265 struct vnode *dvp;
266 struct ncvhashhead *nvcpp;
267
268 if (!doingcache)
269 goto out;
270
271 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
272
273 for (ncp = nvcpp->lh_first; ncp != 0; ncp = ncp->nc_vhash.le_next) {
274 if ((ncp->nc_vp == vp) &&
275 (ncp->nc_vpid == vp->v_id) &&
276 ((dvp = ncp->nc_dvp) != 0) &&
277 (dvp != vp) && /* avoid pesky . entries.. */
278 (dvp->v_id == ncp->nc_dvpid))
279 {
280 char *bp;
281
282 #ifdef DIAGNOSTIC
283 if ((ncp->nc_nlen == 1) &&
284 (ncp->nc_name[0] == '.'))
285 panic("cache_revlookup: found entry for .");
286
287 if ((ncp->nc_nlen == 2) &&
288 (ncp->nc_name[0] == '.') &&
289 (ncp->nc_name[1] == '.'))
290 panic("cache_revlookup: found entry for ..");
291 #endif
292 nchstats.ncs_revhits++;
293
294 if (bufp) {
295 bp = *bpp;
296 bp -= ncp->nc_nlen;
297 if (bp <= bufp) {
298 *dvpp = 0;
299 return ERANGE;
300 }
301 memcpy(bp, ncp->nc_name, ncp->nc_nlen);
302 *bpp = bp;
303 }
304
305 /* XXX MP: how do we know dvp won't evaporate? */
306 *dvpp = dvp;
307 return 0;
308 }
309 }
310 nchstats.ncs_revmiss++;
311 out:
312 *dvpp = 0;
313 return -1;
314 }
315
316 /*
317 * Add an entry to the cache
318 */
319 void
320 cache_enter(dvp, vp, cnp)
321 struct vnode *dvp;
322 struct vnode *vp;
323 struct componentname *cnp;
324 {
325 register struct namecache *ncp;
326 register struct nchashhead *ncpp;
327 register struct ncvhashhead *nvcpp;
328
329 #ifdef DIAGNOSTIC
330 if (cnp->cn_namelen > NCHNAMLEN)
331 panic("cache_enter: name too long");
332 #endif
333 if (!doingcache)
334 return;
335 /*
336 * Free the cache slot at head of lru chain.
337 */
338 if (numcache < desiredvnodes) {
339 ncp = pool_get(&namecache_pool, PR_WAITOK);
340 memset((char *)ncp, 0, sizeof(*ncp));
341 numcache++;
342 } else if ((ncp = nclruhead.tqh_first) != NULL) {
343 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
344 if (ncp->nc_hash.le_prev != 0) {
345 LIST_REMOVE(ncp, nc_hash);
346 ncp->nc_hash.le_prev = 0;
347 }
348 if (ncp->nc_vhash.le_prev != 0) {
349 LIST_REMOVE(ncp, nc_vhash);
350 ncp->nc_vhash.le_prev = 0;
351 }
352 } else
353 return;
354 /* grab the vnode we just found */
355 ncp->nc_vp = vp;
356 if (vp)
357 ncp->nc_vpid = vp->v_id;
358 else {
359 /*
360 * For negative hits, save the ISWHITEOUT flag so we can
361 * restore it later when the cache entry is used again.
362 */
363 ncp->nc_vpid = cnp->cn_flags & ISWHITEOUT;
364 }
365 /* fill in cache info */
366 ncp->nc_dvp = dvp;
367 ncp->nc_dvpid = dvp->v_id;
368 ncp->nc_nlen = cnp->cn_namelen;
369 memcpy(ncp->nc_name, cnp->cn_nameptr, (unsigned)ncp->nc_nlen);
370 TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
371 ncpp = &nchashtbl[(cnp->cn_hash ^ dvp->v_id) & nchash];
372 LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
373
374 ncp->nc_vhash.le_prev = 0;
375 ncp->nc_vhash.le_next = 0;
376
377 /*
378 * Create reverse-cache entries (used in getcwd) for directories.
379 */
380 if (vp &&
381 (vp != dvp) &&
382 (vp->v_type == VDIR) &&
383 ((ncp->nc_nlen > 2) ||
384 ((ncp->nc_nlen == 2) && (ncp->nc_name[0] != '.') && (ncp->nc_name[1] != '.')) ||
385 ((ncp->nc_nlen == 1) && (ncp->nc_name[0] != '.'))))
386 {
387 nvcpp = &ncvhashtbl[(vp->v_id & ncvhash)];
388 LIST_INSERT_HEAD(nvcpp, ncp, nc_vhash);
389 }
390
391 }
392
393 /*
394 * Name cache initialization, from vfs_init() when we are booting
395 */
396 void
397 nchinit()
398 {
399
400 TAILQ_INIT(&nclruhead);
401 nchashtbl = hashinit(desiredvnodes, M_CACHE, M_WAITOK, &nchash);
402 ncvhashtbl = hashinit(desiredvnodes/8, M_CACHE, M_WAITOK, &ncvhash);
403 pool_init(&namecache_pool, sizeof(struct namecache), 0, 0, 0,
404 "ncachepl", 0, pool_page_alloc_nointr, pool_page_free_nointr,
405 M_CACHE);
406 }
407
408 /*
409 * Cache flush, a particular vnode; called when a vnode is renamed to
410 * hide entries that would now be invalid
411 */
412 void
413 cache_purge(vp)
414 struct vnode *vp;
415 {
416 struct namecache *ncp;
417 struct nchashhead *ncpp;
418
419 vp->v_id = ++nextvnodeid;
420 if (nextvnodeid != 0)
421 return;
422 for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
423 for (ncp = ncpp->lh_first; ncp != 0; ncp = ncp->nc_hash.le_next) {
424 ncp->nc_vpid = 0;
425 ncp->nc_dvpid = 0;
426 }
427 }
428 vp->v_id = ++nextvnodeid;
429 }
430
431 /*
432 * Cache flush, a whole filesystem; called when filesys is umounted to
433 * remove entries that would now be invalid
434 *
435 * The line "nxtcp = nchhead" near the end is to avoid potential problems
436 * if the cache lru chain is modified while we are dumping the
437 * inode. This makes the algorithm O(n^2), but do you think I care?
438 */
439 void
440 cache_purgevfs(mp)
441 struct mount *mp;
442 {
443 register struct namecache *ncp, *nxtcp;
444
445 for (ncp = nclruhead.tqh_first; ncp != 0; ncp = nxtcp) {
446 if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) {
447 nxtcp = ncp->nc_lru.tqe_next;
448 continue;
449 }
450 /* free the resources we had */
451 ncp->nc_vp = NULL;
452 ncp->nc_dvp = NULL;
453 TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
454 if (ncp->nc_hash.le_prev != 0) {
455 LIST_REMOVE(ncp, nc_hash);
456 ncp->nc_hash.le_prev = 0;
457 }
458 if (ncp->nc_vhash.le_prev != 0) {
459 LIST_REMOVE(ncp, nc_vhash);
460 ncp->nc_vhash.le_prev = 0;
461 }
462 /* cause rescan of list, it may have altered */
463 nxtcp = nclruhead.tqh_first;
464 TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);
465 }
466 }
467
468