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
11 ** This implements the directory to name cache lookup.
12 ** Given a directory scache and a name, the dnlc returns the
13 ** vcache corresponding to the name. The vcache entries that
14 ** exist in the dnlc are not refcounted.
18 #include <afs/param.h>
30 osi_rwlock_t cm_dnlcLock;
32 static cm_dnlcstats_t dnlcstats; /* dnlc statistics */
33 static int cm_useDnlc = 1; /* yes, start using the dnlc */
34 static int cm_debugDnlc = 0; /* debug dnlc */
37 /* Hash table invariants:
38 * 1. If nameHash[i] is NULL, list is empty
39 * 2. A single element in a hash bucket has itself as prev and next.
42 /* Must be called with cm_dnlcLock write locked */
46 static unsigned int nameptr = 0; /* next bucket to pull something from */
50 if (cm_data.ncfreelist)
52 tnc = cm_data.ncfreelist;
53 cm_data.ncfreelist = tnc->next;
57 for (j=0; j<NHSIZE+2; j++, nameptr++)
59 if (nameptr >= NHSIZE)
61 if (cm_data.nameHash[nameptr])
65 tnc = cm_data.nameHash[nameptr];
68 osi_Log0(afsd_logp,"null tnc in GetMeAnEntry");
73 { /* only thing in list, don't screw around */
74 cm_data.nameHash[nameptr] = (cm_nc_t *) 0;
78 tnc = tnc->prev; /* grab oldest one in this bucket */
79 tnc->next->prev = tnc->prev;/* remove it from list */
80 tnc->prev->next = tnc->next;
86 InsertEntry(cm_nc_t *tnc)
89 key = tnc->key & (NHSIZE -1);
91 if (!cm_data.nameHash[key])
93 cm_data.nameHash[key] = tnc;
94 tnc->next = tnc->prev = tnc;
98 tnc->next = cm_data.nameHash[key];
99 tnc->prev = tnc->next->prev;
100 tnc->next->prev = tnc;
101 tnc->prev->next = tnc;
102 cm_data.nameHash[key] = tnc;
108 cm_dnlcEnter ( cm_scache_t *adp,
113 unsigned int key, skey, new=0;
121 osi_Log3(afsd_logp,"cm_dnlcEnter dir %x name %s scache %x",
122 adp, osi_LogSaveString(afsd_logp,aname), avc);
124 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
125 if (ts - aname >= CM_AFSNCNAMESIZE)
127 skey = key & (NHSIZE -1);
129 lock_ObtainWrite(&cm_dnlcLock);
132 for (tnc = cm_data.nameHash[skey], safety=0; tnc; tnc = tnc->next, safety++ )
133 if ((tnc->dirp == adp) && (!strcmp(tnc->name, aname)))
134 break; /* preexisting entry */
135 else if ( tnc->next == cm_data.nameHash[skey]) /* end of list */
140 else if (safety > NCSIZE)
143 lock_ReleaseWrite(&cm_dnlcLock);
146 osi_Log0(afsd_logp, "DNLC cycle");
153 new = 1; /* entry does not exist, we are creating a new entry*/
154 tnc = GetMeAnEntry();
161 memcpy (tnc->name, aname, ts-aname+1); /* include the NULL */
163 if ( new ) /* insert entry only if it is newly created */
167 lock_ReleaseWrite(&cm_dnlcLock);
174 * if the scache entry is found, return it held
177 cm_dnlcLookup (cm_scache_t *adp, cm_lookupSearch_t* sp)
180 unsigned int key, skey;
181 char* aname = sp->searchNamep;
183 cm_nc_t * tnc, * tnc_begin;
189 osi_Log2(afsd_logp, "cm_dnlcLookup dir %x name %s",
190 adp, osi_LogSaveString(afsd_logp,aname));
192 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
193 if (ts - aname >= CM_AFSNCNAMESIZE)
196 skey = key & (NHSIZE -1);
198 lock_ObtainRead(&cm_dnlcLock);
199 dnlcstats.lookups++; /* Is a dnlcread lock sufficient? */
202 tnc_begin = cm_data.nameHash[skey];
203 for ( tvc = (cm_scache_t *) 0, tnc = tnc_begin, safety=0;
204 tnc; tnc = tnc->next, safety++ )
206 if (tnc->dirp == adp)
209 osi_Log1(afsd_logp,"Looking at [%s]",
210 osi_LogSaveString(afsd_logp,tnc->name));
212 if ( sp->caseFold ) /* case insensitive */
214 match = cm_stricmp(tnc->name, aname);
215 if ( !match ) /* something matches */
220 /* determine what type of match it is */
221 if ( !strcmp(tnc->name, aname))
227 osi_Log1(afsd_logp,"DNLC found exact match [%s]",
228 osi_LogSaveString(afsd_logp,tnc->name));
231 else if ( cm_NoneUpper(tnc->name))
233 else if ( cm_NoneLower(tnc->name))
237 /* Don't break here. We might find an exact match yet */
240 else /* case sensitive */
242 match = strcmp(tnc->name, aname);
243 if ( !match ) /* found a match */
252 if (tnc->next == cm_data.nameHash[skey])
256 else if (tnc->next == tnc_begin || safety > NCSIZE)
259 lock_ReleaseRead(&cm_dnlcLock);
262 osi_Log0(afsd_logp, "DNLC cycle");
268 if(cm_debugDnlc && ts) {
269 osi_Log3(afsd_logp, "DNLC matched [%s] for [%s] with vnode[%ld]",
270 osi_LogSaveString(afsd_logp,ts),
271 osi_LogSaveString(afsd_logp,aname),
272 (long) tvc->fid.vnode);
276 dnlcstats.misses++; /* Is a dnlcread lock sufficient? */
280 sp->fid.vnode = tvc->fid.vnode;
281 sp->fid.unique = tvc->fid.unique;
283 lock_ReleaseRead(&cm_dnlcLock);
288 if ( cm_debugDnlc && tvc )
289 osi_Log1(afsd_logp, "cm_dnlcLookup found %x", tvc);
296 RemoveEntry (cm_nc_t *tnc, unsigned int key)
298 if (!tnc->prev) /* things on freelist always have null prev ptrs */
300 osi_Log0(afsd_logp,"Bogus dnlc freelist");
301 return 1; /* error */
304 if (tnc == tnc->next) /* only one in list */
305 cm_data.nameHash[key] = (cm_nc_t *) 0;
308 if (tnc == cm_data.nameHash[key])
309 cm_data.nameHash[key] = tnc->next;
310 tnc->prev->next = tnc->next;
311 tnc->next->prev = tnc->prev;
314 memset(tnc, 0, sizeof(cm_nc_t));
315 tnc->magic = CM_DNLC_MAGIC;
316 return 0; /* success */
321 cm_dnlcRemove (cm_scache_t *adp, char *aname)
323 unsigned int key, skey, error=0;
324 int found= 0, safety;
332 osi_Log2(afsd_logp, "cm_dnlcRemove dir %x name %s",
333 adp, osi_LogSaveString(afsd_logp,aname));
335 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
336 if (ts - aname >= CM_AFSNCNAMESIZE)
339 skey = key & (NHSIZE -1);
340 lock_ObtainWrite(&cm_dnlcLock);
343 for (tnc = cm_data.nameHash[skey], safety=0; tnc; safety++)
345 if ( (tnc->dirp == adp) && (tnc->key == key)
346 && !strcmp(tnc->name,aname) )
349 error = RemoveEntry(tnc, skey);
353 tnc->next = cm_data.ncfreelist; /* insert entry into freelist */
354 cm_data.ncfreelist = tnc;
355 found = 1; /* found at least one entry */
357 tnc = tmp; /* continue down the linked list */
359 else if (tnc->next == cm_data.nameHash[skey]) /* end of list */
363 if ( safety > NCSIZE )
366 lock_ReleaseWrite(&cm_dnlcLock);
369 osi_Log0(afsd_logp, "DNLC cycle");
374 lock_ReleaseWrite(&cm_dnlcLock);
376 if (!found && !error && cm_debugDnlc)
377 osi_Log0(afsd_logp, "cm_dnlcRemove name not found");
383 /* remove anything pertaining to this directory */
385 cm_dnlcPurgedp (cm_scache_t *adp)
394 osi_Log1(afsd_logp, "cm_dnlcPurgedp dir %x", adp);
396 lock_ObtainWrite(&cm_dnlcLock);
399 for (i=0; i<NCSIZE && !err; i++)
401 if (cm_data.nameCache[i].dirp == adp )
403 if (cm_data.nameCache[i].prev) {
404 err = RemoveEntry(&cm_data.nameCache[i], cm_data.nameCache[i].key & (NHSIZE-1));
407 cm_data.nameCache[i].next = cm_data.ncfreelist;
408 cm_data.ncfreelist = &cm_data.nameCache[i];
411 cm_data.nameCache[i].dirp = cm_data.nameCache[i].vp = (cm_scache_t *) 0;
415 lock_ReleaseWrite(&cm_dnlcLock);
420 /* remove anything pertaining to this file */
422 cm_dnlcPurgevp (cm_scache_t *avc)
431 osi_Log1(afsd_logp, "cm_dnlcPurgevp scache %x", avc);
433 lock_ObtainWrite(&cm_dnlcLock);
436 for (i=0; i<NCSIZE && !err ; i++)
438 if (cm_data.nameCache[i].vp == avc)
440 if (cm_data.nameCache[i].prev) {
441 err = RemoveEntry(&cm_data.nameCache[i], cm_data.nameCache[i].key & (NHSIZE-1));
444 cm_data.nameCache[i].next = cm_data.ncfreelist;
445 cm_data.ncfreelist = &cm_data.nameCache[i];
448 cm_data.nameCache[i].dirp = cm_data.nameCache[i].vp = (cm_scache_t *) 0;
452 lock_ReleaseWrite(&cm_dnlcLock);
457 /* remove everything */
458 void cm_dnlcPurge(void)
466 osi_Log0(afsd_logp, "cm_dnlcPurge");
468 lock_ObtainWrite(&cm_dnlcLock);
471 cm_data.ncfreelist = (cm_nc_t *) 0;
472 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
473 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
474 for (i=0; i<NCSIZE; i++)
476 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
477 cm_data.nameCache[i].next = cm_data.ncfreelist;
478 cm_data.ncfreelist = &cm_data.nameCache[i];
480 lock_ReleaseWrite(&cm_dnlcLock);
484 /* remove everything referencing a specific volume */
485 /* is this function ever called? */
487 cm_dnlcPurgeVol(AFSFid *fidp)
493 dnlcstats.purgevols++;
498 cm_dnlcValidate(void)
504 // are all nameCache entries marked with the magic bit?
505 for (i=0; i<NCSIZE; i++)
507 if (cm_data.nameCache[i].magic != CM_DNLC_MAGIC) {
508 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].magic != CM_DNLC_MAGIC", i);
509 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].magic != CM_DNLC_MAGIC\n", i);
512 if (cm_data.nameCache[i].next &&
513 cm_data.nameCache[i].next->magic != CM_DNLC_MAGIC) {
514 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].next->magic != CM_DNLC_MAGIC", i);
515 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].next->magic != CM_DNLC_MAGIC\n", i);
518 if (cm_data.nameCache[i].prev &&
519 cm_data.nameCache[i].prev->magic != CM_DNLC_MAGIC) {
520 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].prev->magic != CM_DNLC_MAGIC", i);
521 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].prev->magic != CM_DNLC_MAGIC\n", i);
524 if (cm_data.nameCache[i].dirp &&
525 cm_data.nameCache[i].dirp->magic != CM_SCACHE_MAGIC) {
526 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].dirp->magic != CM_SCACHE_MAGIC", i);
527 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].dirp->magic != CM_SCACHE_MAGIC\n", i);
530 if (cm_data.nameCache[i].vp &&
531 cm_data.nameCache[i].vp->magic != CM_SCACHE_MAGIC) {
532 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].vp->magic != CM_SCACHE_MAGIC", i);
533 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].vp->magic != CM_SCACHE_MAGIC\n", i);
538 // are the contents of the hash table intact?
539 for (i=0; i<NHSIZE;i++) {
540 for (ncp = cm_data.nameHash[i]; ncp ;
541 ncp = ncp->next != cm_data.nameHash[i] ? ncp->next : NULL) {
542 if (ncp->magic != CM_DNLC_MAGIC) {
543 afsi_log("cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC");
544 fprintf(stderr, "cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC\n");
547 if (ncp->prev && ncp->prev->magic != CM_DNLC_MAGIC) {
548 afsi_log("cm_dnlcValidate failure: ncp->prev->magic != CM_DNLC_MAGIC");
549 fprintf(stderr, "cm_dnlcValidate failure: ncp->prev->magic != CM_DNLC_MAGIC\n");
552 if (ncp->dirp && ncp->dirp->magic != CM_SCACHE_MAGIC) {
553 afsi_log("cm_dnlcValidate failure: ncp->dirp->magic != CM_DNLC_MAGIC");
554 fprintf(stderr, "cm_dnlcValidate failure: ncp->dirp->magic != CM_DNLC_MAGIC\n");
557 if (ncp->vp && ncp->vp->magic != CM_SCACHE_MAGIC) {
558 afsi_log("cm_dnlcValidate failure: ncp->vp->magic != CM_DNLC_MAGIC");
559 fprintf(stderr, "cm_dnlcValidate failure: ncp->vp->magic != CM_DNLC_MAGIC\n");
565 // is the freelist stable?
566 if ( cm_data.ncfreelist ) {
567 for (ncp = cm_data.ncfreelist, i = 0; ncp && i < NCSIZE;
568 ncp = ncp->next != cm_data.ncfreelist ? ncp->next : NULL, i++) {
569 if (ncp->magic != CM_DNLC_MAGIC) {
570 afsi_log("cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC");
571 fprintf(stderr, "cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC\n");
575 afsi_log("cm_dnlcValidate failure: ncp->prev != NULL");
576 fprintf(stderr, "cm_dnlcValidate failure: ncp->prev != NULL\n");
580 afsi_log("cm_dnlcValidate failure: ncp->key != 0");
581 fprintf(stderr, "cm_dnlcValidate failure: ncp->key != 0\n");
585 afsi_log("cm_dnlcValidate failure: ncp->dirp != NULL");
586 fprintf(stderr, "cm_dnlcValidate failure: ncp->dirp != NULL\n");
590 afsi_log("cm_dnlcValidate failure: ncp->vp != NULL");
591 fprintf(stderr, "cm_dnlcValidate failure: ncp->vp != NULL\n");
596 if ( i == NCSIZE && ncp ) {
597 afsi_log("cm_dnlcValidate failure: dnlc freeList corrupted");
598 fprintf(stderr, "cm_dnlcValidate failure: dnlc freeList corrupted\n");
608 afsi_log("cm_dnlcValidate information: purging");
609 fprintf(stderr, "cm_dnlcValidate information: purging\n");
610 cm_data.ncfreelist = (cm_nc_t *) 0;
611 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
612 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
613 for (i=0; i<NCSIZE; i++)
615 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
616 cm_data.nameCache[i].next = cm_data.ncfreelist;
617 cm_data.ncfreelist = &cm_data.nameCache[i];
624 cm_dnlcInit(int newFile)
632 osi_Log0(afsd_logp,"cm_dnlcInit");
634 memset (&dnlcstats, 0, sizeof(dnlcstats));
636 lock_InitializeRWLock(&cm_dnlcLock, "cm_dnlcLock");
638 lock_ObtainWrite(&cm_dnlcLock);
639 cm_data.ncfreelist = (cm_nc_t *) 0;
640 cm_data.nameCache = cm_data.dnlcBaseAddress;
641 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
642 cm_data.nameHash = (cm_nc_t **) (cm_data.nameCache + NCSIZE);
643 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
645 for (i=0; i<NCSIZE; i++)
647 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
648 cm_data.nameCache[i].next = cm_data.ncfreelist;
649 cm_data.ncfreelist = &cm_data.nameCache[i];
651 lock_ReleaseWrite(&cm_dnlcLock);
656 cm_dnlcShutdown(void)
659 osi_Log0(afsd_logp,"cm_dnlcShutdown");