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