Pass flags to vinvalbuf so it can optionally keep indirect blocks.
[unix-history] / usr / src / sys / kern / vfs_cache.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 *
7 * @(#)vfs_cache.c 7.15 (Berkeley) %G%
8 */
9
10#include "param.h"
11#include "systm.h"
12#include "time.h"
13#include "mount.h"
14#include "vnode.h"
15#include "namei.h"
16#include "errno.h"
17#include "malloc.h"
18
19/*
20 * Name caching works as follows:
21 *
22 * Names found by directory scans are retained in a cache
23 * for future reference. It is managed LRU, so frequently
24 * used names will hang around. Cache is indexed by hash value
25 * obtained from (vp, name) where vp refers to the directory
26 * containing name.
27 *
28 * For simplicity (and economy of storage), names longer than
29 * a maximum length of NCHNAMLEN are not cached; they occur
30 * infrequently in any case, and are almost never of interest.
31 *
32 * Upon reaching the last segment of a path, if the reference
33 * is for DELETE, or NOCACHE is set (rewrite), and the
34 * name is located in the cache, it will be dropped.
35 */
36
37/*
38 * Structures associated with name cacheing.
39 */
40struct namecache **nchashtbl;
41u_long nchash; /* size of hash table - 1 */
42long numcache; /* number of cache entries allocated */
43struct namecache *nchhead, **nchtail; /* LRU chain pointers */
44struct nchstats nchstats; /* cache effectiveness statistics */
45
46int doingcache = 1; /* 1 => enable the cache */
47
48/*
49 * Look for a the name in the cache. We don't do this
50 * if the segment name is long, simply so the cache can avoid
51 * holding long names (which would either waste space, or
52 * add greatly to the complexity).
53 *
54 * Lookup is called with ni_dvp pointing to the directory to search,
55 * ni_ptr pointing to the name of the entry being sought, ni_namelen
56 * tells the length of the name, and ni_hash contains a hash of
57 * the name. If the lookup succeeds, the vnode is returned in ni_vp
58 * and a status of -1 is returned. If the lookup determines that
59 * the name does not exist (negative cacheing), a status of ENOENT
60 * is returned. If the lookup fails, a status of zero is returned.
61 */
62int
63cache_lookup(dvp, vpp, cnp)
64 struct vnode *dvp;
65 struct vnode **vpp;
66 struct componentname *cnp;
67{
68 register struct namecache *ncp, *ncq, **ncpp;
69
70 if (!doingcache)
71 return (0);
72 if (cnp->cn_namelen > NCHNAMLEN) {
73 nchstats.ncs_long++;
74 cnp->cn_flags &= ~MAKEENTRY;
75 return (0);
76 }
77 ncpp = &nchashtbl[cnp->cn_hash & nchash];
78 for (ncp = *ncpp; ncp; ncp = ncp->nc_forw) {
79 if (ncp->nc_dvp == dvp &&
80 ncp->nc_dvpid == dvp->v_id &&
81 ncp->nc_nlen == cnp->cn_namelen &&
82 !bcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
83 break;
84 }
85 if (ncp == NULL) {
86 nchstats.ncs_miss++;
87 return (0);
88 }
89 if (!(cnp->cn_flags & MAKEENTRY)) {
90 nchstats.ncs_badhits++;
91 } else if (ncp->nc_vp == NULL) {
92 if (cnp->cn_nameiop != CREATE) {
93 nchstats.ncs_neghits++;
94 /*
95 * Move this slot to end of LRU chain,
96 * if not already there.
97 */
98 if (ncp->nc_nxt) {
99 /* remove from LRU chain */
100 *ncp->nc_prev = ncp->nc_nxt;
101 ncp->nc_nxt->nc_prev = ncp->nc_prev;
102 /* and replace at end of it */
103 ncp->nc_nxt = NULL;
104 ncp->nc_prev = nchtail;
105 *nchtail = ncp;
106 nchtail = &ncp->nc_nxt;
107 }
108 return (ENOENT);
109 }
110 } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
111 nchstats.ncs_falsehits++;
112 } else {
113 nchstats.ncs_goodhits++;
114 /*
115 * move this slot to end of LRU chain, if not already there
116 */
117 if (ncp->nc_nxt) {
118 /* remove from LRU chain */
119 *ncp->nc_prev = ncp->nc_nxt;
120 ncp->nc_nxt->nc_prev = ncp->nc_prev;
121 /* and replace at end of it */
122 ncp->nc_nxt = NULL;
123 ncp->nc_prev = nchtail;
124 *nchtail = ncp;
125 nchtail = &ncp->nc_nxt;
126 }
127 *vpp = ncp->nc_vp;
128 return (-1);
129 }
130
131 /*
132 * Last component and we are renaming or deleting,
133 * the cache entry is invalid, or otherwise don't
134 * want cache entry to exist.
135 */
136 /* remove from LRU chain */
137 if (ncq = ncp->nc_nxt)
138 ncq->nc_prev = ncp->nc_prev;
139 else
140 nchtail = ncp->nc_prev;
141 *ncp->nc_prev = ncq;
142 /* remove from hash chain */
143 if (ncq = ncp->nc_forw)
144 ncq->nc_back = ncp->nc_back;
145 *ncp->nc_back = ncq;
146 /* and make a dummy hash chain */
147 ncp->nc_forw = NULL;
148 ncp->nc_back = NULL;
149 /* insert at head of LRU list (first to grab) */
150 if (ncq = nchhead)
151 ncq->nc_prev = &ncp->nc_nxt;
152 else
153 nchtail = &ncp->nc_nxt;
154 nchhead = ncp;
155 ncp->nc_nxt = ncq;
156 ncp->nc_prev = &nchhead;
157 return (0);
158}
159
160/*
161 * Add an entry to the cache
162 */
163cache_enter(dvp, vp, cnp)
164 struct vnode *dvp;
165 struct vnode *vp;
166 struct componentname *cnp;
167{
168 register struct namecache *ncp, *ncq, **ncpp;
169
170#ifdef DIAGNOSTIC
171 if (cnp->cn_namelen > NCHNAMLEN)
172 panic("cache_enter: name too long");
173#endif
174 if (!doingcache)
175 return;
176 /*
177 * Free the cache slot at head of lru chain.
178 */
179 if (numcache < desiredvnodes) {
180 ncp = (struct namecache *)
181 malloc((u_long)sizeof *ncp, M_CACHE, M_WAITOK);
182 bzero((char *)ncp, sizeof *ncp);
183 numcache++;
184 } else if (ncp = nchhead) {
185 /* remove from lru chain */
186 if (ncq = ncp->nc_nxt)
187 ncq->nc_prev = ncp->nc_prev;
188 else
189 nchtail = ncp->nc_prev;
190 *ncp->nc_prev = ncq;
191 /* remove from old hash chain, if on one */
192 if (ncp->nc_back) {
193 if (ncq = ncp->nc_forw)
194 ncq->nc_back = ncp->nc_back;
195 *ncp->nc_back = ncq;
196 ncp->nc_forw = NULL;
197 ncp->nc_back = NULL;
198 }
199 } else
200 return;
201 /* grab the vnode we just found */
202 ncp->nc_vp = vp;
203 if (vp)
204 ncp->nc_vpid = vp->v_id;
205 else
206 ncp->nc_vpid = 0;
207 /* fill in cache info */
208 ncp->nc_dvp = dvp;
209 ncp->nc_dvpid = dvp->v_id;
210 ncp->nc_nlen = cnp->cn_namelen;
211 bcopy(cnp->cn_nameptr, 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 ncpp = &nchashtbl[cnp->cn_hash & nchash];
219 if (ncq = *ncpp)
220 ncq->nc_back = &ncp->nc_forw;
221 ncp->nc_forw = ncq;
222 ncp->nc_back = ncpp;
223 *ncpp = ncp;
224}
225
226/*
227 * Name cache initialization, from vfs_init() when we are booting
228 */
229nchinit()
230{
231
232 nchtail = &nchhead;
233 nchashtbl = hashinit(desiredvnodes, M_CACHE, &nchash);
234}
235
236/*
237 * Cache flush, a particular vnode; called when a vnode is renamed to
238 * hide entries that would now be invalid
239 */
240cache_purge(vp)
241 struct vnode *vp;
242{
243 struct namecache *ncp, **ncpp;
244
245 vp->v_id = ++nextvnodeid;
246 if (nextvnodeid != 0)
247 return;
248 for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
249 for (ncp = *ncpp; ncp; ncp = ncp->nc_forw) {
250 ncp->nc_vpid = 0;
251 ncp->nc_dvpid = 0;
252 }
253 }
254 vp->v_id = ++nextvnodeid;
255}
256
257/*
258 * Cache flush, a whole filesystem; called when filesys is umounted to
259 * remove entries that would now be invalid
260 *
261 * The line "nxtcp = nchhead" near the end is to avoid potential problems
262 * if the cache lru chain is modified while we are dumping the
263 * inode. This makes the algorithm O(n^2), but do you think I care?
264 */
265cache_purgevfs(mp)
266 struct mount *mp;
267{
268 register struct namecache *ncp, *nxtcp;
269
270 for (ncp = nchhead; ncp; ncp = nxtcp) {
271 if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp) {
272 nxtcp = ncp->nc_nxt;
273 continue;
274 }
275 /* free the resources we had */
276 ncp->nc_vp = NULL;
277 ncp->nc_dvp = NULL;
278 /* remove from old hash chain, if on one */
279 if (ncp->nc_back) {
280 if (nxtcp = ncp->nc_forw)
281 nxtcp->nc_back = ncp->nc_back;
282 *ncp->nc_back = nxtcp;
283 ncp->nc_forw = NULL;
284 ncp->nc_back = NULL;
285 }
286 /* delete this entry from LRU chain */
287 if (nxtcp = ncp->nc_nxt)
288 nxtcp->nc_prev = ncp->nc_prev;
289 else
290 nchtail = ncp->nc_prev;
291 *ncp->nc_prev = nxtcp;
292 /* cause rescan of list, it may have altered */
293 /* also put the now-free entry at head of LRU */
294 if (nxtcp = nchhead)
295 nxtcp->nc_prev = &ncp->nc_nxt;
296 else
297 nchtail = &ncp->nc_nxt;
298 nchhead = ncp;
299 ncp->nc_nxt = nxtcp;
300 ncp->nc_prev = &nchhead;
301 }
302}