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