Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / afs / afs_osidnlc.c
CommitLineData
805e021f
CE
1/*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10/*-
11 * OSI Directory Name Lookup Cache
12 *
13 * Keep a local cache of lookup results to avoid needing to examine the disk
14 * cache, for frequently accessed names.
15 */
16
17#include <afsconfig.h>
18#include "afs/param.h"
19
20
21#include "afs/sysincludes.h" /*Standard vendor system headers */
22#include "afsincludes.h" /*AFS-based standard headers */
23#include "afs/afs.h"
24#include "afs/lock.h"
25#include "afs/afs_stats.h"
26#include "afs/afs_osidnlc.h"
27
28/* Things to do:
29 * also cache failed lookups.
30 * look into interactions of dnlc and readdir.
31 * cache larger names, perhaps by using a better,longer key (SHA) and discarding
32 * the actual name itself.
33 * precompute a key and stuff for \sys, and combine the HandleAtName function with
34 * this, since we're looking at the name anyway.
35 */
36
37struct afs_lock afs_xdnlc;
38extern struct afs_lock afs_xvcache;
39
40dnlcstats_t dnlcstats;
41
42#define NCSIZE 4096
43#define NHSIZE 256 /* must be power of 2. == NHASHENT in dir package */
44struct nc *ncfreelist = NULL;
45static struct nc nameCache[NCSIZE];
46struct nc *nameHash[NHSIZE];
47/* Hash table invariants:
48 * 1. If nameHash[i] is NULL, list is empty
49 * 2. A single element in a hash bucket has itself as prev and next.
50 */
51
52typedef enum { osi_dnlc_enterT, InsertEntryT, osi_dnlc_lookupT,
53 ScavengeEntryT, osi_dnlc_removeT, RemoveEntryT, osi_dnlc_purgedpT,
54 osi_dnlc_purgevpT, osi_dnlc_purgeT
55} traceevt;
56
57int afs_usednlc = 1;
58
59struct dt {
60 traceevt event;
61 unsigned char slot;
62};
63
64struct dt dnlctracetable[256];
65int dnlct;
66
67#define TRACE(e,s) /* if (dnlct == 256) dnlct = 0; dnlctracetable[dnlct].event = e; dnlctracetable[dnlct++].slot = s; */
68
69#define dnlcHash(ts, hval) for (hval=0; *ts; ts++) { hval *= 173; hval += *ts; }
70
71static struct nc *
72GetMeAnEntry(void)
73{
74 static unsigned int nameptr = 0; /* next bucket to pull something from */
75 struct nc *tnc;
76 int j;
77
78 if (ncfreelist) {
79 tnc = ncfreelist;
80 ncfreelist = tnc->next;
81 return tnc;
82 }
83
84 for (j = 0; j < NHSIZE + 2; j++, nameptr++) {
85 if (nameptr >= NHSIZE)
86 nameptr = 0;
87 if (nameHash[nameptr])
88 break;
89 }
90
91 if (nameptr >= NHSIZE)
92 nameptr = 0;
93
94 TRACE(ScavengeEntryT, nameptr);
95 tnc = nameHash[nameptr];
96 if (!tnc) /* May want to consider changing this to return 0 */
97 osi_Panic("null tnc in GetMeAnEntry");
98
99 if (tnc->prev == tnc) { /* only thing in list, don't screw around */
100 nameHash[nameptr] = NULL;
101 return (tnc);
102 }
103
104 tnc = tnc->prev; /* grab oldest one in this bucket */
105 /* remove it from list */
106 tnc->next->prev = tnc->prev;
107 tnc->prev->next = tnc->next;
108
109 return (tnc);
110}
111
112static void
113InsertEntry(struct nc *tnc)
114{
115 unsigned int key;
116 key = tnc->key & (NHSIZE - 1);
117
118 TRACE(InsertEntryT, key);
119 if (!nameHash[key]) {
120 nameHash[key] = tnc;
121 tnc->next = tnc->prev = tnc;
122 } else {
123 tnc->next = nameHash[key];
124 tnc->prev = tnc->next->prev;
125 tnc->next->prev = tnc;
126 tnc->prev->next = tnc;
127 nameHash[key] = tnc;
128 }
129}
130
131
132int
133osi_dnlc_enter(struct vcache *adp, char *aname, struct vcache *avc,
134 afs_hyper_t * avno)
135{
136 struct nc *tnc;
137 unsigned int key, skey;
138 char *ts = aname;
139 int safety;
140
141 if (!afs_usednlc)
142 return 0;
143
144 TRACE(osi_dnlc_enterT, 0);
145 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
146 if (ts - aname >= AFSNCNAMESIZE) {
147 return 0;
148 }
149 skey = key & (NHSIZE - 1);
150 dnlcstats.enters++;
151
152 retry:
153 ObtainWriteLock(&afs_xdnlc, 222);
154
155 /* Only cache entries from the latest version of the directory */
156 if (!(adp->f.states & CStatd) || !hsame(*avno, adp->f.m.DataVersion)) {
157 ReleaseWriteLock(&afs_xdnlc);
158 return 0;
159 }
160
161 /*
162 * Make sure each directory entry gets cached no more than once.
163 */
164 for (tnc = nameHash[skey], safety = 0; tnc; tnc = tnc->next, safety++) {
165 if ((tnc->dirp == adp) && (!strcmp((char *)tnc->name, aname))) {
166 /* duplicate entry */
167 break;
168 } else if (tnc->next == nameHash[skey]) { /* end of list */
169 tnc = NULL;
170 break;
171 } else if (safety > NCSIZE) {
172 afs_warn("DNLC cycle");
173 dnlcstats.cycles++;
174 ReleaseWriteLock(&afs_xdnlc);
175 osi_dnlc_purge();
176 goto retry;
177 }
178 }
179
180 if (tnc == NULL) {
181 tnc = GetMeAnEntry();
182
183 tnc->dirp = adp;
184 tnc->vp = avc;
185 tnc->key = key;
186 memcpy((char *)tnc->name, aname, ts - aname + 1); /* include the NULL */
187
188 InsertEntry(tnc);
189 } else {
190 /* duplicate */
191 tnc->vp = avc;
192 }
193 ReleaseWriteLock(&afs_xdnlc);
194
195 return 0;
196}
197
198
199struct vcache *
200osi_dnlc_lookup(struct vcache *adp, char *aname, int locktype)
201{
202 struct vcache *tvc;
203 unsigned int key, skey;
204 char *ts = aname;
205 struct nc *tnc;
206 int safety;
207#ifdef AFS_DARWIN80_ENV
208 vnode_t tvp;
209#endif
210
211 if (!afs_usednlc)
212 return 0;
213
214 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
215 if (ts - aname >= AFSNCNAMESIZE)
216 return 0;
217 skey = key & (NHSIZE - 1);
218
219 TRACE(osi_dnlc_lookupT, skey);
220 dnlcstats.lookups++;
221
222 ObtainReadLock(&afs_xvcache);
223 ObtainReadLock(&afs_xdnlc);
224
225 for (tvc = NULL, tnc = nameHash[skey], safety = 0; tnc;
226 tnc = tnc->next, safety++) {
227 if ( /* (tnc->key == key) && */ (tnc->dirp == adp)
228 && (!strcmp((char *)tnc->name, aname))) {
229 tvc = tnc->vp;
230 break;
231 } else if (tnc->next == nameHash[skey]) { /* end of list */
232 break;
233 } else if (safety > NCSIZE) {
234 afs_warn("DNLC cycle");
235 dnlcstats.cycles++;
236 ReleaseReadLock(&afs_xdnlc);
237 ReleaseReadLock(&afs_xvcache);
238 osi_dnlc_purge();
239 return (0);
240 }
241 }
242
243 ReleaseReadLock(&afs_xdnlc);
244
245 if (!tvc) {
246 ReleaseReadLock(&afs_xvcache);
247 dnlcstats.misses++;
248 } else {
249 if ((tvc->f.states & CVInit)
250#ifdef AFS_DARWIN80_ENV
251 ||(tvc->f.states & CDeadVnode)
252#endif
253 )
254 {
255 ReleaseReadLock(&afs_xvcache);
256 dnlcstats.misses++;
257 osi_dnlc_remove(adp, aname, tvc);
258 return 0;
259 }
260#if defined(AFS_DARWIN80_ENV)
261 tvp = AFSTOV(tvc);
262 if (vnode_get(tvp)) {
263 ReleaseReadLock(&afs_xvcache);
264 dnlcstats.misses++;
265 osi_dnlc_remove(adp, aname, tvc);
266 return 0;
267 }
268 if (vnode_ref(tvp)) {
269 ReleaseReadLock(&afs_xvcache);
270 AFS_GUNLOCK();
271 vnode_put(tvp);
272 AFS_GLOCK();
273 dnlcstats.misses++;
274 osi_dnlc_remove(adp, aname, tvc);
275 return 0;
276 }
277#else
278 osi_vnhold(tvc, 0);
279#endif
280 ReleaseReadLock(&afs_xvcache);
281
282 }
283
284 return tvc;
285}
286
287
288static void
289RemoveEntry(struct nc *tnc, unsigned int key)
290{
291 if (!tnc->prev) /* things on freelist always have null prev ptrs */
292 osi_Panic("bogus free list");
293
294 TRACE(RemoveEntryT, key);
295 if (tnc == tnc->next) { /* only one in list */
296 nameHash[key] = NULL;
297 } else {
298 if (tnc == nameHash[key])
299 nameHash[key] = tnc->next;
300 tnc->prev->next = tnc->next;
301 tnc->next->prev = tnc->prev;
302 }
303
304 tnc->prev = NULL; /* everything not in hash table has 0 prev */
305 tnc->key = 0; /* just for safety's sake */
306}
307
308
309int
310osi_dnlc_remove(struct vcache *adp, char *aname, struct vcache *avc)
311{
312 unsigned int key, skey;
313 char *ts = aname;
314 struct nc *tnc;
315
316 if (!afs_usednlc)
317 return 0;
318
319 TRACE(osi_dnlc_removeT, skey);
320 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
321 if (ts - aname >= AFSNCNAMESIZE) {
322 return 0;
323 }
324 skey = key & (NHSIZE - 1);
325 TRACE(osi_dnlc_removeT, skey);
326 dnlcstats.removes++;
327 ObtainReadLock(&afs_xdnlc);
328
329 for (tnc = nameHash[skey]; tnc; tnc = tnc->next) {
330 if ((tnc->dirp == adp) && (tnc->key == key)
331 && (!strcmp((char *)tnc->name, aname))) {
332 tnc->dirp = NULL; /* now it won't match anything */
333 break;
334 } else if (tnc->next == nameHash[skey]) { /* end of list */
335 tnc = NULL;
336 break;
337 }
338 }
339 ReleaseReadLock(&afs_xdnlc);
340
341 if (!tnc)
342 return 0;
343
344 /* there is a little race condition here, but it's relatively
345 * harmless. At worst, I wind up removing a mapping that I just
346 * created. */
347 if (EWOULDBLOCK == NBObtainWriteLock(&afs_xdnlc, 1)) {
348 return 0; /* no big deal, tnc will get recycled eventually */
349 }
350 RemoveEntry(tnc, skey);
351 tnc->next = ncfreelist;
352 ncfreelist = tnc;
353 ReleaseWriteLock(&afs_xdnlc);
354
355 return 0;
356}
357
358/*!
359 * Remove anything pertaining to this directory. I can invalidate
360 * things without the lock, since I am just looking through the array,
361 * but to move things off the lists or into the freelist, I need the
362 * write lock
363 *
364 * \param adp vcache entry for the directory to be purged.
365 * \return 0
366 */
367int
368osi_dnlc_purgedp(struct vcache *adp)
369{
370 int i;
371 int writelocked;
372
373#ifdef AFS_DARWIN_ENV
374 if (!(adp->f.states & (CVInit | CVFlushed
375#ifdef AFS_DARWIN80_ENV
376 | CDeadVnode
377#endif
378 )) && AFSTOV(adp))
379 cache_purge(AFSTOV(adp));
380#endif
381
382 if (!afs_usednlc)
383 return 0;
384
385 dnlcstats.purgeds++;
386 TRACE(osi_dnlc_purgedpT, 0);
387 writelocked = (0 == NBObtainWriteLock(&afs_xdnlc, 2));
388
389 for (i = 0; i < NCSIZE; i++) {
390 if ((nameCache[i].dirp == adp) || (nameCache[i].vp == adp)) {
391 nameCache[i].dirp = nameCache[i].vp = NULL;
392 if (writelocked && nameCache[i].prev) {
393 RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE - 1));
394 nameCache[i].next = ncfreelist;
395 ncfreelist = &nameCache[i];
396 }
397 }
398 }
399 if (writelocked)
400 ReleaseWriteLock(&afs_xdnlc);
401
402 return 0;
403}
404
405/*!
406 * Remove anything pertaining to this file
407 *
408 * \param File vcache entry.
409 * \return 0
410 */
411int
412osi_dnlc_purgevp(struct vcache *avc)
413{
414 int i;
415 int writelocked;
416
417#ifdef AFS_DARWIN_ENV
418 if (!(avc->f.states & (CVInit | CVFlushed
419#ifdef AFS_DARWIN80_ENV
420 | CDeadVnode
421#endif
422 )) && AFSTOV(avc))
423 cache_purge(AFSTOV(avc));
424#endif
425
426 if (!afs_usednlc)
427 return 0;
428
429 dnlcstats.purgevs++;
430 TRACE(osi_dnlc_purgevpT, 0);
431 writelocked = (0 == NBObtainWriteLock(&afs_xdnlc, 3));
432
433 for (i = 0; i < NCSIZE; i++) {
434 if (nameCache[i].vp == avc) {
435 nameCache[i].dirp = nameCache[i].vp = NULL;
436 /* can't simply break; because of hard links -- might be two */
437 /* different entries with same vnode */
438 if (writelocked && nameCache[i].prev) {
439 RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE - 1));
440 nameCache[i].next = ncfreelist;
441 ncfreelist = &nameCache[i];
442 }
443 }
444 }
445 if (writelocked)
446 ReleaseWriteLock(&afs_xdnlc);
447
448 return 0;
449}
450
451/* remove everything */
452int
453osi_dnlc_purge(void)
454{
455 int i;
456
457 dnlcstats.purges++;
458 TRACE(osi_dnlc_purgeT, 0);
459 if (EWOULDBLOCK == NBObtainWriteLock(&afs_xdnlc, 4)) { /* couldn't get lock */
460 for (i = 0; i < NCSIZE; i++)
461 nameCache[i].dirp = nameCache[i].vp = NULL;
462 } else { /* did get the lock */
463 ncfreelist = NULL;
464 memset(nameCache, 0, sizeof(struct nc) * NCSIZE);
465 memset(nameHash, 0, sizeof(struct nc *) * NHSIZE);
466 for (i = 0; i < NCSIZE; i++) {
467 nameCache[i].next = ncfreelist;
468 ncfreelist = &nameCache[i];
469 }
470 ReleaseWriteLock(&afs_xdnlc);
471 }
472
473 return 0;
474}
475
476/* remove everything referencing a specific volume */
477int
478osi_dnlc_purgevol(struct VenusFid *fidp)
479{
480
481 dnlcstats.purgevols++;
482 osi_dnlc_purge();
483
484 return 0;
485}
486
487int
488osi_dnlc_init(void)
489{
490 int i;
491
492 Lock_Init(&afs_xdnlc);
493 memset(&dnlcstats, 0, sizeof(dnlcstats));
494 memset(dnlctracetable, 0, sizeof(dnlctracetable));
495 dnlct = 0;
496 ObtainWriteLock(&afs_xdnlc, 223);
497 ncfreelist = NULL;
498 memset(nameCache, 0, sizeof(struct nc) * NCSIZE);
499 memset(nameHash, 0, sizeof(struct nc *) * NHSIZE);
500 for (i = 0; i < NCSIZE; i++) {
501 nameCache[i].next = ncfreelist;
502 ncfreelist = &nameCache[i];
503 }
504 ReleaseWriteLock(&afs_xdnlc);
505
506 return 0;
507}
508
509int
510osi_dnlc_shutdown(void)
511{
512 return 0;
513}