|
|
1.1 root 1: /*
2: * Copyright (c) 1989 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: *
33: * @(#)vfs_cache.c 7.8 (Berkeley) 2/28/91
34: */
35:
36: #include "param.h"
37: #include "systm.h"
38: #include "time.h"
39: #include "mount.h"
40: #include "vnode.h"
41: #include "namei.h"
42: #include "errno.h"
43: #include "malloc.h"
44:
45: /*
46: * Name caching works as follows:
47: *
48: * Names found by directory scans are retained in a cache
49: * for future reference. It is managed LRU, so frequently
50: * used names will hang around. Cache is indexed by hash value
51: * obtained from (vp, name) where vp refers to the directory
52: * containing name.
53: *
54: * For simplicity (and economy of storage), names longer than
55: * a maximum length of NCHNAMLEN are not cached; they occur
56: * infrequently in any case, and are almost never of interest.
57: *
58: * Upon reaching the last segment of a path, if the reference
59: * is for DELETE, or NOCACHE is set (rewrite), and the
60: * name is located in the cache, it will be dropped.
61: */
62:
63: /*
64: * Structures associated with name cacheing.
65: */
66: union nchash {
67: union nchash *nch_head[2];
68: struct namecache *nch_chain[2];
69: } *nchashtbl;
70: #define nch_forw nch_chain[0]
71: #define nch_back nch_chain[1]
72:
73: u_long nchash; /* size of hash table - 1 */
74: long numcache; /* number of cache entries allocated */
75: struct namecache *nchhead, **nchtail; /* LRU chain pointers */
76: struct nchstats nchstats; /* cache effectiveness statistics */
77:
78: int doingcache = 1; /* 1 => enable the cache */
79:
80: /*
81: * Look for a the name in the cache. We don't do this
82: * if the segment name is long, simply so the cache can avoid
83: * holding long names (which would either waste space, or
84: * add greatly to the complexity).
85: *
86: * Lookup is called with ni_dvp pointing to the directory to search,
87: * ni_ptr pointing to the name of the entry being sought, ni_namelen
88: * tells the length of the name, and ni_hash contains a hash of
89: * the name. If the lookup succeeds, the vnode is returned in ni_vp
90: * and a status of -1 is returned. If the lookup determines that
91: * the name does not exist (negative cacheing), a status of ENOENT
92: * is returned. If the lookup fails, a status of zero is returned.
93: */
94: cache_lookup(ndp)
95: register struct nameidata *ndp;
96: {
97: register struct vnode *dvp;
98: register struct namecache *ncp;
99: union nchash *nhp;
100:
101: if (!doingcache)
102: return (0);
103: if (ndp->ni_namelen > NCHNAMLEN) {
104: nchstats.ncs_long++;
105: ndp->ni_makeentry = 0;
106: return (0);
107: }
108: dvp = ndp->ni_dvp;
109: nhp = &nchashtbl[ndp->ni_hash & nchash];
110: for (ncp = nhp->nch_forw; ncp != (struct namecache *)nhp;
111: ncp = ncp->nc_forw) {
112: if (ncp->nc_dvp == dvp &&
113: ncp->nc_dvpid == dvp->v_id &&
114: ncp->nc_nlen == ndp->ni_namelen &&
115: !bcmp(ncp->nc_name, ndp->ni_ptr, (unsigned)ncp->nc_nlen))
116: break;
117: }
118: if (ncp == (struct namecache *)nhp) {
119: nchstats.ncs_miss++;
120: return (0);
121: }
122: if (!ndp->ni_makeentry) {
123: nchstats.ncs_badhits++;
124: } else if (ncp->nc_vp == NULL) {
125: if ((ndp->ni_nameiop & OPMASK) != CREATE) {
126: nchstats.ncs_neghits++;
127: /*
128: * Move this slot to end of LRU chain,
129: * if not already there.
130: */
131: if (ncp->nc_nxt) {
132: /* remove from LRU chain */
133: *ncp->nc_prev = ncp->nc_nxt;
134: ncp->nc_nxt->nc_prev = ncp->nc_prev;
135: /* and replace at end of it */
136: ncp->nc_nxt = NULL;
137: ncp->nc_prev = nchtail;
138: *nchtail = ncp;
139: nchtail = &ncp->nc_nxt;
140: }
141: return (ENOENT);
142: }
143: } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
144: nchstats.ncs_falsehits++;
145: } else {
146: nchstats.ncs_goodhits++;
147: /*
148: * move this slot to end of LRU chain, if not already there
149: */
150: if (ncp->nc_nxt) {
151: /* remove from LRU chain */
152: *ncp->nc_prev = ncp->nc_nxt;
153: ncp->nc_nxt->nc_prev = ncp->nc_prev;
154: /* and replace at end of it */
155: ncp->nc_nxt = NULL;
156: ncp->nc_prev = nchtail;
157: *nchtail = ncp;
158: nchtail = &ncp->nc_nxt;
159: }
160: ndp->ni_vp = ncp->nc_vp;
161: return (-1);
162: }
163:
164: /*
165: * Last component and we are renaming or deleting,
166: * the cache entry is invalid, or otherwise don't
167: * want cache entry to exist.
168: */
169: /* remove from LRU chain */
170: *ncp->nc_prev = ncp->nc_nxt;
171: if (ncp->nc_nxt)
172: ncp->nc_nxt->nc_prev = ncp->nc_prev;
173: else
174: nchtail = ncp->nc_prev;
175: /* remove from hash chain */
176: remque(ncp);
177: /* insert at head of LRU list (first to grab) */
178: ncp->nc_nxt = nchhead;
179: ncp->nc_prev = &nchhead;
180: nchhead->nc_prev = &ncp->nc_nxt;
181: nchhead = ncp;
182: /* and make a dummy hash chain */
183: ncp->nc_forw = ncp;
184: ncp->nc_back = ncp;
185: return (0);
186: }
187:
188: /*
189: * Add an entry to the cache
190: */
191: cache_enter(ndp)
192: register struct nameidata *ndp;
193: {
194: register struct namecache *ncp;
195: union nchash *nhp;
196:
197: if (!doingcache)
198: return;
199: /*
200: * Free the cache slot at head of lru chain.
201: */
202: if (numcache < desiredvnodes) {
203: ncp = (struct namecache *)
204: malloc((u_long)sizeof *ncp, M_CACHE, M_WAITOK);
205: bzero((char *)ncp, sizeof *ncp);
206: numcache++;
207: } else if (ncp = nchhead) {
208: /* remove from lru chain */
209: *ncp->nc_prev = ncp->nc_nxt;
210: if (ncp->nc_nxt)
211: ncp->nc_nxt->nc_prev = ncp->nc_prev;
212: else
213: nchtail = ncp->nc_prev;
214: /* remove from old hash chain */
215: remque(ncp);
216: } else
217: return;
218: /* grab the vnode we just found */
219: ncp->nc_vp = ndp->ni_vp;
220: if (ndp->ni_vp)
221: ncp->nc_vpid = ndp->ni_vp->v_id;
222: else
223: ncp->nc_vpid = 0;
224: /* fill in cache info */
225: ncp->nc_dvp = ndp->ni_dvp;
226: ncp->nc_dvpid = ndp->ni_dvp->v_id;
227: ncp->nc_nlen = ndp->ni_namelen;
228: bcopy(ndp->ni_ptr, ncp->nc_name, (unsigned)ncp->nc_nlen);
229: /* link at end of lru chain */
230: ncp->nc_nxt = NULL;
231: ncp->nc_prev = nchtail;
232: *nchtail = ncp;
233: nchtail = &ncp->nc_nxt;
234: /* and insert on hash chain */
235: nhp = &nchashtbl[ndp->ni_hash & nchash];
236: insque(ncp, nhp);
237: }
238:
239: /*
240: * Name cache initialization, from vfs_init() when we are booting
241: */
242: nchinit()
243: {
244: register union nchash *nchp;
245: long nchashsize;
246:
247: nchhead = 0;
248: nchtail = &nchhead;
249: nchashsize = roundup((desiredvnodes + 1) * sizeof *nchp / 2,
250: NBPG * CLSIZE);
251: nchashtbl = (union nchash *)malloc((u_long)nchashsize,
252: M_CACHE, M_WAITOK);
253: for (nchash = 1; nchash <= nchashsize / sizeof *nchp; nchash <<= 1)
254: /* void */;
255: nchash = (nchash >> 1) - 1;
256: for (nchp = &nchashtbl[nchash]; nchp >= nchashtbl; nchp--) {
257: nchp->nch_head[0] = nchp;
258: nchp->nch_head[1] = nchp;
259: }
260: }
261:
262: /*
263: * Cache flush, a particular vnode; called when a vnode is renamed to
264: * hide entries that would now be invalid
265: */
266: cache_purge(vp)
267: struct vnode *vp;
268: {
269: union nchash *nhp;
270: struct namecache *ncp;
271:
272: vp->v_id = ++nextvnodeid;
273: if (nextvnodeid != 0)
274: return;
275: for (nhp = &nchashtbl[nchash]; nhp >= nchashtbl; nhp--) {
276: for (ncp = nhp->nch_forw; ncp != (struct namecache *)nhp;
277: ncp = ncp->nc_forw) {
278: ncp->nc_vpid = 0;
279: ncp->nc_dvpid = 0;
280: }
281: }
282: vp->v_id = ++nextvnodeid;
283: }
284:
285: /*
286: * Cache flush, a whole filesystem; called when filesys is umounted to
287: * remove entries that would now be invalid
288: *
289: * The line "nxtcp = nchhead" near the end is to avoid potential problems
290: * if the cache lru chain is modified while we are dumping the
291: * inode. This makes the algorithm O(n^2), but do you think I care?
292: */
293: cache_purgevfs(mp)
294: struct mount *mp;
295: {
296: register struct namecache *ncp, *nxtcp;
297:
298: for (ncp = nchhead; ncp; ncp = nxtcp) {
299: nxtcp = ncp->nc_nxt;
300: if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp)
301: continue;
302: /* free the resources we had */
303: ncp->nc_vp = NULL;
304: ncp->nc_dvp = NULL;
305: remque(ncp); /* remove entry from its hash chain */
306: ncp->nc_forw = ncp; /* and make a dummy one */
307: ncp->nc_back = ncp;
308: /* delete this entry from LRU chain */
309: *ncp->nc_prev = nxtcp;
310: if (nxtcp)
311: nxtcp->nc_prev = ncp->nc_prev;
312: else
313: nchtail = ncp->nc_prev;
314: /* cause rescan of list, it may have altered */
315: nxtcp = nchhead;
316: /* put the now-free entry at head of LRU */
317: ncp->nc_nxt = nxtcp;
318: ncp->nc_prev = &nchhead;
319: nxtcp->nc_prev = &ncp->nc_nxt;
320: nchhead = ncp;
321: }
322: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.