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>
27 #include <WINNT/afsreg.h>
29 osi_rwlock_t cm_dnlcLock;
31 static cm_dnlcstats_t dnlcstats; /* dnlc statistics */
32 static int cm_useDnlc = 1; /* yes, start using the dnlc */
33 static int cm_debugDnlc = 0; /* debug dnlc */
36 /* Hash table invariants:
37 * 1. If nameHash[i] is NULL, list is empty
38 * 2. A single element in a hash bucket has itself as prev and next.
41 /* Must be called with cm_dnlcLock write locked */
45 static unsigned int nameptr = 0; /* next bucket to pull something from */
49 if (cm_data.ncfreelist)
51 tnc = cm_data.ncfreelist;
52 cm_data.ncfreelist = tnc->next;
56 for (j=0; j<NHSIZE+2; j++, nameptr++)
58 if (nameptr >= NHSIZE)
60 if (cm_data.nameHash[nameptr])
64 tnc = cm_data.nameHash[nameptr];
67 osi_Log0(afsd_logp,"null tnc in GetMeAnEntry");
72 { /* only thing in list, don't screw around */
73 cm_data.nameHash[nameptr] = (cm_nc_t *) 0;
77 tnc = tnc->prev; /* grab oldest one in this bucket */
78 tnc->next->prev = tnc->prev;/* remove it from list */
79 tnc->prev->next = tnc->next;
85 InsertEntry(cm_nc_t *tnc)
88 key = tnc->key & (NHSIZE -1);
90 if (!cm_data.nameHash[key])
92 cm_data.nameHash[key] = tnc;
93 tnc->next = tnc->prev = tnc;
97 tnc->next = cm_data.nameHash[key];
98 tnc->prev = tnc->next->prev;
99 tnc->next->prev = tnc;
100 tnc->prev->next = tnc;
101 cm_data.nameHash[key] = tnc;
107 cm_dnlcEnter ( cm_scache_t *adp,
112 unsigned int key, skey, new=0;
120 if (!strcmp(aname,".") || !strcmp(aname,".."))
124 osi_Log3(afsd_logp,"cm_dnlcEnter dir %x name %s scache %x",
125 adp, osi_LogSaveString(afsd_logp,aname), avc);
127 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
128 if (ts - aname >= CM_AFSNCNAMESIZE)
130 skey = key & (NHSIZE -1);
132 InterlockedIncrement(&dnlcstats.enters);
133 lock_ObtainRead(&cm_dnlcLock);
135 for (tnc = cm_data.nameHash[skey], safety=0; tnc; tnc = tnc->next, safety++ )
136 if ((tnc->dirp == adp) && (!strcmp(tnc->name, aname)))
137 break; /* preexisting entry */
138 else if ( tnc->next == cm_data.nameHash[skey]) /* end of list */
143 else if (safety > NCSIZE)
145 InterlockedIncrement(&dnlcstats.cycles);
147 lock_ReleaseWrite(&cm_dnlcLock);
149 lock_ReleaseRead(&cm_dnlcLock);
152 osi_Log0(afsd_logp, "DNLC cycle");
159 if ( !writeLocked ) {
160 lock_ReleaseRead(&cm_dnlcLock);
161 lock_ObtainWrite(&cm_dnlcLock);
165 new = 1; /* entry does not exist, we are creating a new entry*/
166 tnc = GetMeAnEntry();
173 memcpy (tnc->name, aname, ts-aname+1); /* include the NULL */
175 if ( new ) /* insert entry only if it is newly created */
180 lock_ReleaseWrite(&cm_dnlcLock);
182 lock_ReleaseRead(&cm_dnlcLock);
189 * if the scache entry is found, return it held
192 cm_dnlcLookup (cm_scache_t *adp, cm_lookupSearch_t* sp)
195 unsigned int key, skey;
196 char* aname = sp->searchNamep;
198 cm_nc_t * tnc, * tnc_begin;
205 osi_Log2(afsd_logp, "cm_dnlcLookup dir %x name %s",
206 adp, osi_LogSaveString(afsd_logp,aname));
208 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
210 if (ts - aname >= CM_AFSNCNAMESIZE) {
211 InterlockedIncrement(&dnlcstats.lookups);
212 InterlockedIncrement(&dnlcstats.misses);
216 skey = key & (NHSIZE -1);
218 lock_ObtainRead(&cm_dnlcLock);
219 InterlockedIncrement(&dnlcstats.lookups);
222 tnc_begin = cm_data.nameHash[skey];
223 for ( tvc = (cm_scache_t *) NULL, tnc = tnc_begin, safety=0;
224 tnc; tnc = tnc->next, safety++ )
226 if (tnc->dirp == adp)
229 osi_Log1(afsd_logp,"Looking at [%s]",
230 osi_LogSaveString(afsd_logp,tnc->name));
232 if ( sp->caseFold ) /* case insensitive */
234 match = cm_stricmp(tnc->name, aname);
235 if ( !match ) /* something matches */
240 /* determine what type of match it is */
241 if ( !strcmp(tnc->name, aname))
247 osi_Log1(afsd_logp,"DNLC found exact match [%s]",
248 osi_LogSaveString(afsd_logp,tnc->name));
251 else if ( cm_NoneUpper(tnc->name))
253 else if ( cm_NoneLower(tnc->name))
257 /* Don't break here. We might find an exact match yet */
260 else /* case sensitive */
262 match = strcmp(tnc->name, aname);
263 if ( !match ) /* found a match */
272 if (tnc->next == cm_data.nameHash[skey])
276 else if (tnc->next == tnc_begin || safety > NCSIZE)
278 InterlockedIncrement(&dnlcstats.cycles);
279 lock_ReleaseRead(&cm_dnlcLock);
282 osi_Log0(afsd_logp, "DNLC cycle");
288 if(cm_debugDnlc && ts) {
289 osi_Log3(afsd_logp, "DNLC matched [%s] for [%s] with vnode[%ld]",
290 osi_LogSaveString(afsd_logp,ts),
291 osi_LogSaveString(afsd_logp,aname),
292 (long) tvc->fid.vnode);
296 InterlockedIncrement(&dnlcstats.misses);
300 sp->fid.vnode = tvc->fid.vnode;
301 sp->fid.unique = tvc->fid.unique;
303 lock_ReleaseRead(&cm_dnlcLock);
308 if ( cm_debugDnlc && tvc )
309 osi_Log1(afsd_logp, "cm_dnlcLookup found %x", tvc);
316 RemoveEntry (cm_nc_t *tnc, unsigned int key)
318 if (!tnc->prev) /* things on freelist always have null prev ptrs */
320 osi_Log0(afsd_logp,"Bogus dnlc freelist");
321 return 1; /* error */
324 if (tnc == tnc->next) /* only one in list */
325 cm_data.nameHash[key] = (cm_nc_t *) 0;
328 if (tnc == cm_data.nameHash[key])
329 cm_data.nameHash[key] = tnc->next;
330 tnc->prev->next = tnc->next;
331 tnc->next->prev = tnc->prev;
334 memset(tnc, 0, sizeof(cm_nc_t));
335 tnc->magic = CM_DNLC_MAGIC;
336 return 0; /* success */
341 cm_dnlcRemove (cm_scache_t *adp, char *aname)
343 unsigned int key, skey, error=0;
344 int found= 0, safety;
352 osi_Log2(afsd_logp, "cm_dnlcRemove dir %x name %s",
353 adp, osi_LogSaveString(afsd_logp,aname));
355 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
356 if (ts - aname >= CM_AFSNCNAMESIZE)
359 skey = key & (NHSIZE -1);
360 lock_ObtainWrite(&cm_dnlcLock);
361 InterlockedIncrement(&dnlcstats.removes);
363 for (tnc = cm_data.nameHash[skey], safety=0; tnc; safety++)
365 if ( (tnc->dirp == adp) && (tnc->key == key)
366 && !strcmp(tnc->name,aname) )
369 error = RemoveEntry(tnc, skey);
373 tnc->next = cm_data.ncfreelist; /* insert entry into freelist */
374 cm_data.ncfreelist = tnc;
375 found = 1; /* found at least one entry */
377 tnc = tmp; /* continue down the linked list */
379 else if (tnc->next == cm_data.nameHash[skey]) /* end of list */
383 if ( safety > NCSIZE )
385 InterlockedIncrement(&dnlcstats.cycles);
386 lock_ReleaseWrite(&cm_dnlcLock);
389 osi_Log0(afsd_logp, "DNLC cycle");
394 lock_ReleaseWrite(&cm_dnlcLock);
396 if (!found && !error && cm_debugDnlc)
397 osi_Log0(afsd_logp, "cm_dnlcRemove name not found");
403 /* remove anything pertaining to this directory */
405 cm_dnlcPurgedp (cm_scache_t *adp)
414 osi_Log1(afsd_logp, "cm_dnlcPurgedp dir %x", adp);
416 lock_ObtainWrite(&cm_dnlcLock);
417 InterlockedIncrement(&dnlcstats.purgeds);
419 for (i=0; i<NCSIZE && !err; i++)
421 if (cm_data.nameCache[i].dirp == adp )
423 if (cm_data.nameCache[i].prev) {
424 err = RemoveEntry(&cm_data.nameCache[i], cm_data.nameCache[i].key & (NHSIZE-1));
427 cm_data.nameCache[i].next = cm_data.ncfreelist;
428 cm_data.ncfreelist = &cm_data.nameCache[i];
431 cm_data.nameCache[i].dirp = cm_data.nameCache[i].vp = (cm_scache_t *) 0;
435 lock_ReleaseWrite(&cm_dnlcLock);
440 /* remove anything pertaining to this file */
442 cm_dnlcPurgevp (cm_scache_t *avc)
451 osi_Log1(afsd_logp, "cm_dnlcPurgevp scache %x", avc);
453 lock_ObtainWrite(&cm_dnlcLock);
454 InterlockedIncrement(&dnlcstats.purgevs);
456 for (i=0; i<NCSIZE && !err ; i++)
458 if (cm_data.nameCache[i].vp == avc)
460 if (cm_data.nameCache[i].prev) {
461 err = RemoveEntry(&cm_data.nameCache[i], cm_data.nameCache[i].key & (NHSIZE-1));
464 cm_data.nameCache[i].next = cm_data.ncfreelist;
465 cm_data.ncfreelist = &cm_data.nameCache[i];
468 cm_data.nameCache[i].dirp = cm_data.nameCache[i].vp = (cm_scache_t *) 0;
472 lock_ReleaseWrite(&cm_dnlcLock);
477 /* remove everything */
478 void cm_dnlcPurge(void)
486 osi_Log0(afsd_logp, "cm_dnlcPurge");
488 lock_ObtainWrite(&cm_dnlcLock);
489 InterlockedIncrement(&dnlcstats.purges);
491 cm_data.ncfreelist = (cm_nc_t *) 0;
492 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
493 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
494 for (i=0; i<NCSIZE; i++)
496 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
497 cm_data.nameCache[i].next = cm_data.ncfreelist;
498 cm_data.ncfreelist = &cm_data.nameCache[i];
500 lock_ReleaseWrite(&cm_dnlcLock);
504 /* remove everything referencing a specific volume */
505 /* is this function ever called? */
507 cm_dnlcPurgeVol(AFSFid *fidp)
513 InterlockedIncrement(&dnlcstats.purgevols);
518 cm_dnlcValidate(void)
524 // are all nameCache entries marked with the magic bit?
525 for (i=0; i<NCSIZE; i++)
527 if (cm_data.nameCache[i].magic != CM_DNLC_MAGIC) {
528 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].magic != CM_DNLC_MAGIC", i);
529 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].magic != CM_DNLC_MAGIC\n", i);
532 if (cm_data.nameCache[i].next &&
533 cm_data.nameCache[i].next->magic != CM_DNLC_MAGIC) {
534 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].next->magic != CM_DNLC_MAGIC", i);
535 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].next->magic != CM_DNLC_MAGIC\n", i);
538 if (cm_data.nameCache[i].prev &&
539 cm_data.nameCache[i].prev->magic != CM_DNLC_MAGIC) {
540 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].prev->magic != CM_DNLC_MAGIC", i);
541 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].prev->magic != CM_DNLC_MAGIC\n", i);
544 if (cm_data.nameCache[i].dirp &&
545 cm_data.nameCache[i].dirp->magic != CM_SCACHE_MAGIC) {
546 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].dirp->magic != CM_SCACHE_MAGIC", i);
547 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].dirp->magic != CM_SCACHE_MAGIC\n", i);
550 if (cm_data.nameCache[i].vp &&
551 cm_data.nameCache[i].vp->magic != CM_SCACHE_MAGIC) {
552 afsi_log("cm_dnlcValidate failure: cm_data.nameCache[%d].vp->magic != CM_SCACHE_MAGIC", i);
553 fprintf(stderr, "cm_dnlcValidate failure: cm_data.nameCache[%d].vp->magic != CM_SCACHE_MAGIC\n", i);
558 // are the contents of the hash table intact?
559 for (i=0; i<NHSIZE;i++) {
560 for (ncp = cm_data.nameHash[i]; ncp ;
561 ncp = ncp->next != cm_data.nameHash[i] ? ncp->next : NULL) {
562 if (ncp->magic != CM_DNLC_MAGIC) {
563 afsi_log("cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC");
564 fprintf(stderr, "cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC\n");
567 if (ncp->prev && ncp->prev->magic != CM_DNLC_MAGIC) {
568 afsi_log("cm_dnlcValidate failure: ncp->prev->magic != CM_DNLC_MAGIC");
569 fprintf(stderr, "cm_dnlcValidate failure: ncp->prev->magic != CM_DNLC_MAGIC\n");
572 if (ncp->dirp && ncp->dirp->magic != CM_SCACHE_MAGIC) {
573 afsi_log("cm_dnlcValidate failure: ncp->dirp->magic != CM_DNLC_MAGIC");
574 fprintf(stderr, "cm_dnlcValidate failure: ncp->dirp->magic != CM_DNLC_MAGIC\n");
577 if (ncp->vp && ncp->vp->magic != CM_SCACHE_MAGIC) {
578 afsi_log("cm_dnlcValidate failure: ncp->vp->magic != CM_DNLC_MAGIC");
579 fprintf(stderr, "cm_dnlcValidate failure: ncp->vp->magic != CM_DNLC_MAGIC\n");
585 // is the freelist stable?
586 if ( cm_data.ncfreelist ) {
587 for (ncp = cm_data.ncfreelist, i = 0; ncp && i < NCSIZE;
588 ncp = ncp->next != cm_data.ncfreelist ? ncp->next : NULL, i++) {
589 if (ncp->magic != CM_DNLC_MAGIC) {
590 afsi_log("cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC");
591 fprintf(stderr, "cm_dnlcValidate failure: ncp->magic != CM_DNLC_MAGIC\n");
595 afsi_log("cm_dnlcValidate failure: ncp->prev != NULL");
596 fprintf(stderr, "cm_dnlcValidate failure: ncp->prev != NULL\n");
600 afsi_log("cm_dnlcValidate failure: ncp->key != 0");
601 fprintf(stderr, "cm_dnlcValidate failure: ncp->key != 0\n");
605 afsi_log("cm_dnlcValidate failure: ncp->dirp != NULL");
606 fprintf(stderr, "cm_dnlcValidate failure: ncp->dirp != NULL\n");
610 afsi_log("cm_dnlcValidate failure: ncp->vp != NULL");
611 fprintf(stderr, "cm_dnlcValidate failure: ncp->vp != NULL\n");
616 if ( i == NCSIZE && ncp ) {
617 afsi_log("cm_dnlcValidate failure: dnlc freeList corrupted");
618 fprintf(stderr, "cm_dnlcValidate failure: dnlc freeList corrupted\n");
628 afsi_log("cm_dnlcValidate information: purging");
629 fprintf(stderr, "cm_dnlcValidate information: purging\n");
630 cm_data.ncfreelist = (cm_nc_t *) 0;
631 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
632 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
633 for (i=0; i<NCSIZE; i++)
635 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
636 cm_data.nameCache[i].next = cm_data.ncfreelist;
637 cm_data.ncfreelist = &cm_data.nameCache[i];
644 cm_dnlcInit(int newFile)
652 code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_OPENAFS_SUBKEY,
653 0, KEY_QUERY_VALUE, &parmKey);
654 if (code == ERROR_SUCCESS) {
655 dummyLen = sizeof(DWORD);
656 code = RegQueryValueEx(parmKey, "UseDNLC", NULL, NULL,
657 (BYTE *) &dwValue, &dummyLen);
658 if (code == ERROR_SUCCESS)
659 cm_useDnlc = dwValue ? 1 : 0;
660 afsi_log("CM UseDNLC = %d", cm_useDnlc);
662 dummyLen = sizeof(DWORD);
663 code = RegQueryValueEx(parmKey, "DebugDNLC", NULL, NULL,
664 (BYTE *) &dwValue, &dummyLen);
665 if (code == ERROR_SUCCESS)
666 cm_debugDnlc = dwValue ? 1 : 0;
667 afsi_log("CM DebugDNLC = %d", cm_debugDnlc);
668 RegCloseKey (parmKey);
675 osi_Log0(afsd_logp,"cm_dnlcInit");
677 memset (&dnlcstats, 0, sizeof(dnlcstats));
679 lock_InitializeRWLock(&cm_dnlcLock, "cm_dnlcLock");
681 lock_ObtainWrite(&cm_dnlcLock);
682 cm_data.ncfreelist = (cm_nc_t *) 0;
683 cm_data.nameCache = cm_data.dnlcBaseAddress;
684 memset (cm_data.nameCache, 0, sizeof(cm_nc_t) * NCSIZE);
685 cm_data.nameHash = (cm_nc_t **) (cm_data.nameCache + NCSIZE);
686 memset (cm_data.nameHash, 0, sizeof(cm_nc_t *) * NHSIZE);
688 for (i=0; i<NCSIZE; i++)
690 cm_data.nameCache[i].magic = CM_DNLC_MAGIC;
691 cm_data.nameCache[i].next = cm_data.ncfreelist;
692 cm_data.ncfreelist = &cm_data.nameCache[i];
694 lock_ReleaseWrite(&cm_dnlcLock);
699 cm_dnlcShutdown(void)
702 osi_Log0(afsd_logp,"cm_dnlcShutdown");