2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include "afs/param.h"
16 #include "afs/sysincludes.h" /*Standard vendor system headers */
17 #include "afsincludes.h" /*AFS-based standard headers */
20 #include "afs/afs_stats.h"
21 #include "afs/afs_osidnlc.h"
24 * also cache failed lookups.
25 * look into interactions of dnlc and readdir.
26 * cache larger names, perhaps by using a better,longer key (SHA) and discarding
27 * the actual name itself.
28 * precompute a key and stuff for @sys, and combine the HandleAtName function with
29 * this, since we're looking at the name anyway.
32 struct afs_lock afs_xdnlc;
33 extern struct afs_lock afs_xvcache;
35 dnlcstats_t dnlcstats;
38 #define NHSIZE 256 /* must be power of 2. == NHASHENT in dir package */
39 struct nc *ncfreelist = NULL;
40 static struct nc nameCache[NCSIZE];
41 struct nc *nameHash[NHSIZE];
42 /* Hash table invariants:
43 * 1. If nameHash[i] is NULL, list is empty
44 * 2. A single element in a hash bucket has itself as prev and next.
47 typedef enum { osi_dnlc_enterT, InsertEntryT, osi_dnlc_lookupT,
48 ScavengeEntryT, osi_dnlc_removeT, RemoveEntryT, osi_dnlc_purgedpT,
49 osi_dnlc_purgevpT, osi_dnlc_purgeT
59 struct dt dnlctracetable[256];
62 #define TRACE(e,s) /* if (dnlct == 256) dnlct = 0; dnlctracetable[dnlct].event = e; dnlctracetable[dnlct++].slot = s; */
64 #define dnlcHash(ts, hval) for (hval=0; *ts; ts++) { hval *= 173; hval += *ts; }
69 static unsigned int nameptr = 0; /* next bucket to pull something from */
75 ncfreelist = tnc->next;
79 for (j = 0; j < NHSIZE + 2; j++, nameptr++) {
80 if (nameptr >= NHSIZE)
82 if (nameHash[nameptr])
86 TRACE(ScavengeEntryT, nameptr);
87 tnc = nameHash[nameptr];
88 if (!tnc) /* May want to consider changing this to return 0 */
89 osi_Panic("null tnc in GetMeAnEntry");
91 if (tnc->prev == tnc) { /* only thing in list, don't screw around */
92 nameHash[nameptr] = NULL;
96 tnc = tnc->prev; /* grab oldest one in this bucket */
97 /* remove it from list */
98 tnc->next->prev = tnc->prev;
99 tnc->prev->next = tnc->next;
105 InsertEntry(struct nc *tnc)
108 key = tnc->key & (NHSIZE - 1);
110 TRACE(InsertEntryT, key);
111 if (!nameHash[key]) {
113 tnc->next = tnc->prev = tnc;
115 tnc->next = nameHash[key];
116 tnc->prev = tnc->next->prev;
117 tnc->next->prev = tnc;
118 tnc->prev->next = tnc;
125 osi_dnlc_enter(struct vcache *adp, char *aname, struct vcache *avc,
129 unsigned int key, skey;
136 TRACE(osi_dnlc_enterT, 0);
137 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
138 if (ts - aname >= AFSNCNAMESIZE) {
141 skey = key & (NHSIZE - 1);
145 ObtainWriteLock(&afs_xdnlc, 222);
147 /* Only cache entries from the latest version of the directory */
148 if (!(adp->states & CStatd) || !hsame(*avno, adp->m.DataVersion)) {
149 ReleaseWriteLock(&afs_xdnlc);
154 * Make sure each directory entry gets cached no more than once.
156 for (tnc = nameHash[skey], safety = 0; tnc; tnc = tnc->next, safety++) {
157 if ((tnc->dirp == adp) && (!strcmp((char *)tnc->name, aname))) {
158 /* duplicate entry */
160 } else if (tnc->next == nameHash[skey]) { /* end of list */
163 } else if (safety > NCSIZE) {
164 afs_warn("DNLC cycle");
166 ReleaseWriteLock(&afs_xdnlc);
173 tnc = GetMeAnEntry();
178 memcpy((char *)tnc->name, aname, ts - aname + 1); /* include the NULL */
185 ReleaseWriteLock(&afs_xdnlc);
192 osi_dnlc_lookup(struct vcache *adp, char *aname, int locktype)
196 unsigned int key, skey;
198 struct nc *tnc, *tnc1 = 0;
200 #ifdef AFS_DARWIN80_ENV
207 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
208 if (ts - aname >= AFSNCNAMESIZE) {
211 skey = key & (NHSIZE - 1);
213 TRACE(osi_dnlc_lookupT, skey);
216 ObtainReadLock(&afs_xvcache);
217 ObtainReadLock(&afs_xdnlc);
219 for (tvc = NULL, tnc = nameHash[skey], safety = 0; tnc;
220 tnc = tnc->next, safety++) {
221 if ( /* (tnc->key == key) && */ (tnc->dirp == adp)
222 && (!strcmp((char *)tnc->name, aname))) {
226 } else if (tnc->next == nameHash[skey]) { /* end of list */
228 } else if (safety > NCSIZE) {
229 afs_warn("DNLC cycle");
231 ReleaseReadLock(&afs_xdnlc);
232 ReleaseReadLock(&afs_xvcache);
238 LRUme = 0; /* (tnc != nameHash[skey]); */
239 ReleaseReadLock(&afs_xdnlc);
242 ReleaseReadLock(&afs_xvcache);
245 if (tvc->states & CVInit) {
246 ReleaseReadLock(&afs_xvcache);
248 osi_dnlc_remove(adp, aname, tvc);
252 VN_HOLD((vnode_t *) tvc);
254 #ifdef AFS_DARWIN80_ENV
256 if (vnode_get(tvp)) {
257 ReleaseReadLock(&afs_xvcache);
259 osi_dnlc_remove(adp, aname, tvc);
262 if (vnode_ref(tvp)) {
263 ReleaseReadLock(&afs_xvcache);
268 osi_dnlc_remove(adp, aname, tvc);
275 ReleaseReadLock(&afs_xvcache);
279 * XX If LRUme ever is non-zero change the if statement around because
280 * aix's cc with optimizer on won't necessarily check things in order XX
282 if (LRUme && (0 == NBObtainWriteLock(&afs_xdnlc))) {
283 /* don't block to do this */
284 /* tnc might have been moved during race condition, */
285 /* but it's always in a legit hash chain when a lock is granted,
286 * or else it's on the freelist so prev == NULL,
287 * so at worst this is redundant */
288 /* Now that we've got it held, and a lock on the dnlc, we
289 * should check to be sure that there was no race, and
290 * bail out if there was. */
292 /* special case for only two elements on list - relative ordering
294 if (tnc->prev != tnc->next) {
295 /* remove from old location */
296 tnc->prev->next = tnc->next;
297 tnc->next->prev = tnc->prev;
298 /* insert into new location */
299 tnc->next = nameHash[skey];
300 tnc->prev = tnc->next->prev;
301 tnc->next->prev = tnc;
302 tnc->prev->next = tnc;
304 nameHash[skey] = tnc;
306 ReleaseWriteLock(&afs_xdnlc);
316 RemoveEntry(struct nc *tnc, unsigned int key)
318 if (!tnc->prev) /* things on freelist always have null prev ptrs */
319 osi_Panic("bogus free list");
321 TRACE(RemoveEntryT, key);
322 if (tnc == tnc->next) { /* only one in list */
323 nameHash[key] = NULL;
325 if (tnc == nameHash[key])
326 nameHash[key] = tnc->next;
327 tnc->prev->next = tnc->next;
328 tnc->next->prev = tnc->prev;
331 tnc->prev = NULL; /* everything not in hash table has 0 prev */
332 tnc->key = 0; /* just for safety's sake */
337 osi_dnlc_remove(struct vcache *adp, char *aname, struct vcache *avc)
339 unsigned int key, skey;
346 TRACE(osi_dnlc_removeT, skey);
347 dnlcHash(ts, key); /* leaves ts pointing at the NULL */
348 if (ts - aname >= AFSNCNAMESIZE) {
351 skey = key & (NHSIZE - 1);
352 TRACE(osi_dnlc_removeT, skey);
354 ObtainReadLock(&afs_xdnlc);
356 for (tnc = nameHash[skey]; tnc; tnc = tnc->next) {
357 if ((tnc->dirp == adp) && (tnc->key == key)
358 && (!strcmp((char *)tnc->name, aname))) {
359 tnc->dirp = NULL; /* now it won't match anything */
361 } else if (tnc->next == nameHash[skey]) { /* end of list */
366 ReleaseReadLock(&afs_xdnlc);
371 /* there is a little race condition here, but it's relatively
372 * harmless. At worst, I wind up removing a mapping that I just
374 if (EWOULDBLOCK == NBObtainWriteLock(&afs_xdnlc, 1)) {
375 return 0; /* no big deal, tnc will get recycled eventually */
377 RemoveEntry(tnc, skey);
378 tnc->next = ncfreelist;
380 ReleaseWriteLock(&afs_xdnlc);
385 /* remove anything pertaining to this directory. I can invalidate
386 * things without the lock, since I am just looking through the array,
387 * but to move things off the lists or into the freelist, I need the
390 osi_dnlc_purgedp(struct vcache *adp)
395 #ifdef AFS_DARWIN_ENV
396 if (!(adp->states & (CVInit | CVFlushed
397 #ifdef AFS_DARWIN80_ENV
401 cache_purge(AFSTOV(adp));
408 TRACE(osi_dnlc_purgedpT, 0);
409 writelocked = (0 == NBObtainWriteLock(&afs_xdnlc, 2));
411 for (i = 0; i < NCSIZE; i++) {
412 if ((nameCache[i].dirp == adp) || (nameCache[i].vp == adp)) {
413 nameCache[i].dirp = nameCache[i].vp = NULL;
414 if (writelocked && nameCache[i].prev) {
415 RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE - 1));
416 nameCache[i].next = ncfreelist;
417 ncfreelist = &nameCache[i];
422 ReleaseWriteLock(&afs_xdnlc);
427 /* remove anything pertaining to this file */
429 osi_dnlc_purgevp(struct vcache *avc)
434 #ifdef AFS_DARWIN_ENV
435 if (!(avc->states & (CVInit | CVFlushed
436 #ifdef AFS_DARWIN80_ENV
440 cache_purge(AFSTOV(avc));
447 TRACE(osi_dnlc_purgevpT, 0);
448 writelocked = (0 == NBObtainWriteLock(&afs_xdnlc, 3));
450 for (i = 0; i < NCSIZE; i++) {
451 if ((nameCache[i].vp == avc)) {
452 nameCache[i].dirp = nameCache[i].vp = NULL;
453 /* can't simply break; because of hard links -- might be two */
454 /* different entries with same vnode */
455 if (writelocked && nameCache[i].prev) {
456 RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE - 1));
457 nameCache[i].next = ncfreelist;
458 ncfreelist = &nameCache[i];
463 ReleaseWriteLock(&afs_xdnlc);
468 /* remove everything */
475 TRACE(osi_dnlc_purgeT, 0);
476 if (EWOULDBLOCK == NBObtainWriteLock(&afs_xdnlc, 4)) { /* couldn't get lock */
477 for (i = 0; i < NCSIZE; i++)
478 nameCache[i].dirp = nameCache[i].vp = NULL;
479 } else { /* did get the lock */
481 memset((char *)nameCache, 0, sizeof(struct nc) * NCSIZE);
482 memset((char *)nameHash, 0, sizeof(struct nc *) * NHSIZE);
483 for (i = 0; i < NCSIZE; i++) {
484 nameCache[i].next = ncfreelist;
485 ncfreelist = &nameCache[i];
487 ReleaseWriteLock(&afs_xdnlc);
493 /* remove everything referencing a specific volume */
495 osi_dnlc_purgevol(struct VenusFid *fidp)
498 dnlcstats.purgevols++;
509 Lock_Init(&afs_xdnlc);
510 memset((char *)&dnlcstats, 0, sizeof(dnlcstats));
511 memset((char *)dnlctracetable, 0, sizeof(dnlctracetable));
513 ObtainWriteLock(&afs_xdnlc, 223);
515 memset((char *)nameCache, 0, sizeof(struct nc) * NCSIZE);
516 memset((char *)nameHash, 0, sizeof(struct nc *) * NHSIZE);
517 for (i = 0; i < NCSIZE; i++) {
518 nameCache[i].next = ncfreelist;
519 ncfreelist = &nameCache[i];
521 ReleaseWriteLock(&afs_xdnlc);
527 osi_dnlc_shutdown(void)