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 static 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.
41 static struct nc *ncfreelist = (struct nc *)0;
42 static struct nc nameCache[NCSIZE];
43 static struct nc *nameHash[NHSIZE];
46 #define dnlcNotify(x,debug){ \
51 hh = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME); \
52 ReportEvent(hh,EVENTLOG_ERROR_TYPE,0,__LINE__, \
53 NULL, 1, 0, ptbuf, NULL); \
54 DeregisterEventSource(hh); \
58 #define dnlcNotify(x,debug)
64 static unsigned int nameptr = 0; /* next bucket to pull something from */
71 ncfreelist = tnc->next;
75 for (j=0; j<NHSIZE+2; j++, nameptr++)
77 if (nameptr >= NHSIZE)
79 if (nameHash[nameptr])
83 tnc = nameHash[nameptr];
86 dnlcNotify("null tnc in GetMeAnEntry",1);
87 osi_Log0(afsd_logp,"null tnc in GetMeAnEntry");
92 { /* only thing in list, don't screw around */
93 nameHash[nameptr] = (struct nc *) 0;
97 tnc = tnc->prev; /* grab oldest one in this bucket */
98 tnc->next->prev = tnc->prev;/* remove it from list */
99 tnc->prev->next = tnc->next;
109 key = tnc->key & (NHSIZE -1);
114 tnc->next = tnc->prev = tnc;
118 tnc->next = nameHash[key];
119 tnc->prev = tnc->next->prev;
120 tnc->next->prev = tnc;
121 tnc->prev->next = tnc;
128 cm_dnlcEnter ( adp, aname, avc )
134 unsigned int key, skey, new=0;
142 osi_Log3(afsd_logp,"cm_dnlcEnter dir %x name %s scache %x",
143 adp, osi_LogSaveString(afsd_logp,aname), avc);
145 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
146 if (ts - aname >= CM_AFSNCNAMESIZE)
148 skey = key & (NHSIZE -1);
150 lock_ObtainWrite(&cm_dnlcLock);
153 for (tnc = nameHash[skey], safety=0; tnc; tnc = tnc->next, safety++ )
154 if ((tnc->dirp == adp) && (!strcmp(tnc->name, aname)))
155 break; /* preexisting entry */
156 else if ( tnc->next == nameHash[skey]) /* end of list */
161 else if ( safety >NCSIZE)
164 lock_ReleaseWrite(&cm_dnlcLock);
166 dnlcNotify("DNLC cycle",1);
168 osi_Log0(afsd_logp, "DNLC cycle");
175 new = 1; /* entry does not exist, we are creating a new entry*/
176 tnc = GetMeAnEntry();
183 memcpy (tnc->name, aname, ts-aname+1); /* include the NULL */
185 if ( new ) /* insert entry only if it is newly created */
189 lock_ReleaseWrite(&cm_dnlcLock);
196 * if the scache entry is found, return it held
199 cm_dnlcLookup ( adp, sp)
201 cm_lookupSearch_t* sp;
204 unsigned int key, skey;
205 char* aname = sp->searchNamep;
207 struct nc * tnc, * tnc_begin;
213 osi_Log2(afsd_logp, "cm_dnlcLookup dir %x name %s",
214 adp, osi_LogSaveString(afsd_logp,aname));
216 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
217 if (ts - aname >= CM_AFSNCNAMESIZE)
220 skey = key & (NHSIZE -1);
222 lock_ObtainRead(&cm_dnlcLock);
223 dnlcstats.lookups++; /* Is a dnlcread lock sufficient? */
226 tnc_begin = nameHash[skey];
227 for ( tvc = (cm_scache_t *) 0, tnc = tnc_begin, safety=0;
228 tnc; tnc = tnc->next, safety++ )
230 if (tnc->dirp == adp)
233 osi_Log1(afsd_logp,"Looking at [%s]",
234 osi_LogSaveString(afsd_logp,tnc->name));
236 if ( sp->caseFold ) /* case insensitive */
238 match = cm_stricmp(tnc->name, aname);
239 if ( !match ) /* something matches */
244 /* determine what type of match it is */
245 if ( !strcmp(tnc->name, aname))
251 osi_Log1(afsd_logp,"DNLC found exact match [%s]",
252 osi_LogSaveString(afsd_logp,tnc->name));
255 else if ( cm_NoneUpper(tnc->name))
257 else if ( cm_NoneLower(tnc->name))
261 /* Don't break here. We might find an exact match yet */
264 else /* case sensitive */
266 match = strcmp(tnc->name, aname);
267 if ( !match ) /* found a match */
276 if (tnc->next == nameHash[skey])
280 else if (tnc->next == tnc_begin || safety >NCSIZE)
283 lock_ReleaseRead(&cm_dnlcLock);
285 dnlcNotify("DNLC cycle",1);
287 osi_Log0(afsd_logp, "DNLC cycle");
293 if(cm_debugDnlc && ts) {
294 osi_Log3(afsd_logp, "DNLC matched [%s] for [%s] with vnode[%ld]",
295 osi_LogSaveString(afsd_logp,ts),
296 osi_LogSaveString(afsd_logp,aname),
297 (long) tvc->fid.vnode);
301 dnlcstats.misses++; /* Is a dnlcread lock sufficient? */
305 sp->fid.vnode = tvc->fid.vnode;
306 sp->fid.unique = tvc->fid.unique;
308 lock_ReleaseRead(&cm_dnlcLock);
311 lock_ObtainWrite(&cm_scacheLock);
312 tvc->refCount++; /* scache entry held */
313 lock_ReleaseWrite(&cm_scacheLock);
316 if ( cm_debugDnlc && tvc )
317 osi_Log1(afsd_logp, "cm_dnlcLookup found %x", tvc);
324 RemoveEntry (tnc, key)
328 if (!tnc->prev) /* things on freelist always have null prev ptrs */
330 dnlcNotify("Bogus dnlc freelist", 1);
331 osi_Log0(afsd_logp,"Bogus dnlc freelist");
332 return 1; /* error */
335 if (tnc == tnc->next) /* only one in list */
336 nameHash[key] = (struct nc *) 0;
339 if (tnc == nameHash[key])
340 nameHash[key] = tnc->next;
341 tnc->prev->next = tnc->next;
342 tnc->next->prev = tnc->prev;
345 tnc->prev = (struct nc *) 0; /* everything not in hash table has 0 prev */
346 tnc->key = 0; /* just for safety's sake */
347 return 0; /* success */
352 cm_dnlcRemove ( adp, aname)
356 unsigned int key, skey, error=0;
357 int found= 0, safety;
359 struct nc *tnc, *tmp;
365 osi_Log2(afsd_logp, "cm_dnlcRemove dir %x name %s",
366 adp, osi_LogSaveString(afsd_logp,aname));
368 dnlcHash( ts, key ); /* leaves ts pointing at the NULL */
369 if (ts - aname >= CM_AFSNCNAMESIZE)
372 skey = key & (NHSIZE -1);
373 lock_ObtainWrite(&cm_dnlcLock);
376 for (tnc = nameHash[skey], safety=0; tnc; safety++)
378 if ( (tnc->dirp == adp) && (tnc->key == key)
379 && !strcmp(tnc->name,aname) )
381 tnc->dirp = (cm_scache_t *) 0; /* now it won't match anything */
383 error = RemoveEntry(tnc, skey);
387 tnc->next = ncfreelist; /* insert entry into freelist */
389 found = 1; /* found atleast one entry */
391 tnc = tmp; /* continue down the linked list */
393 else if (tnc->next == nameHash[skey]) /* end of list */
397 if ( safety > NCSIZE )
400 lock_ReleaseWrite(&cm_dnlcLock);
402 dnlcNotify("DNLC cycle",1);
404 osi_Log0(afsd_logp, "DNLC cycle");
409 lock_ReleaseWrite(&cm_dnlcLock);
411 if (!found && !error && cm_debugDnlc)
412 osi_Log0(afsd_logp, "cm_dnlcRemove name not found");
418 /* remove anything pertaining to this directory */
430 osi_Log1(afsd_logp, "cm_dnlcPurgedp dir %x", adp);
432 lock_ObtainWrite(&cm_dnlcLock);
435 for (i=0; i<NCSIZE && !err; i++)
437 if (nameCache[i].dirp == adp )
439 nameCache[i].dirp = nameCache[i].vp = (cm_scache_t *) 0;
440 if (nameCache[i].prev && !err)
442 err = RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE-1));
443 nameCache[i].next = ncfreelist;
444 ncfreelist = &nameCache[i];
448 lock_ReleaseWrite(&cm_dnlcLock);
453 /* remove anything pertaining to this file */
455 cm_dnlcPurgevp ( avc )
465 osi_Log1(afsd_logp, "cm_dnlcPurgevp scache %x", avc);
467 lock_ObtainWrite(&cm_dnlcLock);
470 for (i=0; i<NCSIZE && !err ; i++)
472 if (nameCache[i].vp == avc)
474 nameCache[i].dirp = nameCache[i].vp = (cm_scache_t *) 0;
475 /* can't simply break; because of hard links -- might be two */
476 /* different entries with same vnode */
477 if (!err && nameCache[i].prev)
479 err=RemoveEntry(&nameCache[i], nameCache[i].key & (NHSIZE-1));
480 nameCache[i].next = ncfreelist;
481 ncfreelist = &nameCache[i];
485 lock_ReleaseWrite(&cm_dnlcLock);
490 /* remove everything */
491 void cm_dnlcPurge(void)
499 osi_Log0(afsd_logp, "cm_dnlcPurge");
501 lock_ObtainWrite(&cm_dnlcLock);
504 ncfreelist = (struct nc *) 0;
505 memset (nameCache, 0, sizeof(struct nc) * NCSIZE);
506 memset (nameHash, 0, sizeof(struct nc *) * NHSIZE);
507 for (i=0; i<NCSIZE; i++)
509 nameCache[i].next = ncfreelist;
510 ncfreelist = &nameCache[i];
512 lock_ReleaseWrite(&cm_dnlcLock);
516 /* remove everything referencing a specific volume */
518 cm_dnlcPurgeVol( fidp )
525 dnlcstats.purgevols++;
537 osi_Log0(afsd_logp,"cm_dnlcInit");
539 lock_InitializeRWLock(&cm_dnlcLock, "cm_dnlcLock");
540 memset (&dnlcstats, 0, sizeof(dnlcstats));
541 lock_ObtainWrite(&cm_dnlcLock);
542 ncfreelist = (struct nc *) 0;
543 memset (nameCache, 0, sizeof(struct nc) * NCSIZE);
544 memset (nameHash, 0, sizeof(struct nc *) * NHSIZE);
545 for (i=0; i<NCSIZE; i++)
547 nameCache[i].next = ncfreelist;
548 ncfreelist = &nameCache[i];
550 lock_ReleaseWrite(&cm_dnlcLock);
554 cm_dnlcShutdown(void)
557 osi_Log0(afsd_logp,"cm_dnlcShutdown");