Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

767 lines
20 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
namcache.c
Abstract:
The following functions are provided to support name cache management in
mini-rdrs. See namcache.h for a more complete description of how a mini-rdr
could use name caches to help eliminate trips to the server.
Author:
David Orbits [davidor] 9-Sep-1996
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "prefix.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxNameCacheInitialize)
#pragma alloc_text(PAGE, RxNameCacheCreateEntry)
#pragma alloc_text(PAGE, RxNameCacheFetchEntry)
#pragma alloc_text(PAGE, RxNameCacheCheckEntry)
#pragma alloc_text(PAGE, RxNameCacheActivateEntry)
#pragma alloc_text(PAGE, RxNameCacheExpireEntry)
#pragma alloc_text(PAGE, RxNameCacheFreeEntry)
#pragma alloc_text(PAGE, RxNameCacheFinalize)
#pragma alloc_text(PAGE, RxNameCacheExpireEntryWithShortName)
#endif
#define Dbg (DEBUG_TRACE_NAMECACHE)
VOID
RxNameCacheInitialize(
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN ULONG MRxNameCacheSize,
IN ULONG MaximumEntries
)
/*++
Routine Description:
This routine initializes a NAME_CACHE structure.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
allocate the entry.
MRxNameCacheSize - The size in bytes of the mini-rdr portion of the name
cache entry.
MaximumEntries - The maximum number of entries that will ever be
allocated. E.g. This prevents an errant program which
opens tons of files with bad names from chewing up
paged pool.
Return Value:
None.
--*/
{
PAGED_CODE();
ExInitializeFastMutex(&NameCacheCtl->NameCacheLock);
InitializeListHead(&NameCacheCtl->ActiveList);
InitializeListHead(&NameCacheCtl->FreeList);
NameCacheCtl->NumberActivates = 0;
NameCacheCtl->NumberChecks = 0;
NameCacheCtl->NumberNameHits = 0;
NameCacheCtl->NumberNetOpsSaved = 0;
NameCacheCtl->EntryCount = 0;
NameCacheCtl->MaximumEntries = MaximumEntries;
NameCacheCtl->MRxNameCacheSize = MRxNameCacheSize;
return;
}
PNAME_CACHE
RxNameCacheCreateEntry (
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PUNICODE_STRING Name,
IN BOOLEAN CaseInsensitive
)
/*++
Routine Description:
This routine allocates and initializes a NAME_CACHE structure with the
given name string, Lifetime (in seconds) and MRxContext.
It returns a pointer to the name cache structure or NULL if no entry was
available. It is expected that the caller will then initialize any
additional mini-rdr portion of the name cache context and then put the
entry on the name cache active list by calling RxNameCacheActivateEntry().
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
allocate the entry.
Name - A pointer to the unicode name string to initialize the
the entry with.
CaseInsensitive - True if need case insensitive compare on name.
Return Value:
PNAME_CACHE - returns a pointer to the newly allocated NAME_CACHE struct
or NULL if allocation fails.
--*/
{
LONG i;
PNAME_CACHE *NameCacheArray;
PNAME_CACHE NameCache;
ULONG NameCacheSize;
PAGED_CODE();
RxDbgTrace( +1, Dbg, ("RxNameCacheCreateEntry: %wZ\n", Name ));
//
// Grab an entry off the free list.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
if (!IsListEmpty(&NameCacheCtl->FreeList)) {
NameCache = (PNAME_CACHE) RemoveHeadList(&NameCacheCtl->FreeList);
} else {
NameCache = NULL;
}
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
if (NameCache != NULL) {
NameCache = CONTAINING_RECORD(NameCache, NAME_CACHE, Link);
RxDbgTrace(0, Dbg, ("took from free list\n"));
} else {
//
// Didn't get an entry off the free list, allocate one.
// Don't exceed Max but we could go over a little if multiple threads
// are allocating.
//
if (NameCacheCtl->EntryCount < NameCacheCtl->MaximumEntries) {
NameCacheSize = QuadAlign(sizeof(NAME_CACHE)) +
QuadAlign(NameCacheCtl->MRxNameCacheSize);
NameCache = RxAllocatePoolWithTag(
PagedPool,
NameCacheSize,
RX_NAME_CACHE_POOLTAG);
if (NameCache != NULL) {
//
// Init standard header fields, bump entry count & setup
// mini-rdr context extension.
//
ZeroAndInitializeNodeType(
NameCache,
RDBSS_NTC_STORAGE_TYPE_UNKNOWN,
(NODE_BYTE_SIZE) NameCacheSize);
InterlockedIncrement(&NameCacheCtl->EntryCount);
NameCache->Name.Buffer = NULL;
NameCache->Name.Length = 0;
NameCache->Name.MaximumLength = 0;
if (NameCacheCtl->MRxNameCacheSize > 0) {
NameCache->ContextExtension = (PBYTE)NameCache +
QuadAlign(sizeof(NAME_CACHE));
RxDbgTrace(0, Dbg, ("allocated new entry\n"));
}
}
}
//
// If still no entry then bag it.
//
if (NameCache == NULL) {
RxDbgTrace(-1, Dbg, ("Fail no entry allocated!\n"));
return NULL;
}
}
//
// If name won't fit in current string, free it and allocate new string.
//
if (Name->Length > NameCache->Name.MaximumLength) {
if (NameCache->Name.Buffer != NULL) {
RxFreePool(NameCache->Name.Buffer);
}
if (Name->Length > 0) {
NameCache->Name.Buffer = RxAllocatePoolWithTag(
PagedPool,
(ULONG) Name->Length,
RX_NAME_CACHE_POOLTAG);
} else {
NameCache->Name.Buffer = NULL;
}
if (Name->Length > 0 &&
NameCache->Name.Buffer == NULL) {
//
// if didn't get the storage. Zero the string length and put entry
// back on the free list. Otherwise save allocation in max length.
//
NameCache->Name.Length = 0;
NameCache->Name.MaximumLength = 0;
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
RxDbgTrace(-1, Dbg, ("Fail no pool for name!\n"));
return NULL;
} else {
NameCache->Name.MaximumLength = Name->Length;
}
}
//
// Save the name & length. Set the case matching flag. Set the hash field.
//
NameCache->Name.Length = Name->Length;
NameCache->CaseInsensitive = CaseInsensitive;
if (Name->Length > 0) {
RtlMoveMemory(NameCache->Name.Buffer, Name->Buffer, Name->Length);
NameCache->HashValue = RxTableComputeHashValue(&NameCache->Name);
}else {
NameCache->HashValue = 0;
}
RxDbgTrace(-1, Dbg, ("Success!\n"));
return NameCache;
}
PNAME_CACHE
RxNameCacheFetchEntry (
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PUNICODE_STRING Name
)
/*++
Routine Description:
This routine looks for a match in the name cache for Name.
If found the entry is removed from the Name Cache active list and
a pointer to the NAME_CACHE struct is returned. Otherwise NULL is returned.
The entry is removed to avoid problems with another thread trying to
update the same entry or observing that it expired and putting it on the
free list. We could get multiple entries with the same name by different
threads but eventually they will expire.
If a matching entry is found no check is made for expiration. That is left
to the caller since it is likely the caller would want to take a special
action.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
Name - A pointer to the unicode name string to scan for.
Return Value:
PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
Side Effects:
As the active list is scanned any non-matching entries that have expired are
put on the free list.
--*/
{
PNAME_CACHE NameCache = NULL;
PLIST_ENTRY pListEntry;
PLIST_ENTRY ExpiredEntry;
ULONG HashValue;
LARGE_INTEGER CurrentTime;
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
if (Name->Length > 0) {
HashValue = RxTableComputeHashValue(Name);
} else {
HashValue = 0;
}
KeQueryTickCount( &CurrentTime );
NameCacheCtl->NumberChecks += 1;
//
// Get the lock and scan the active list.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
pListEntry = NameCacheCtl->ActiveList.Flink;
while (pListEntry != &NameCacheCtl->ActiveList) {
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
//
// Do initial match on the hash value and the length. Then do full string.
//
if ((NameCache->HashValue == HashValue) &&
(Name->Length == NameCache->Name.Length)) {
if (Name->Length == 0 ||
RtlEqualUnicodeString(
Name,
&NameCache->Name,
NameCache->CaseInsensitive) ) {
//
// Found a match.
//
NameCacheCtl->NumberNameHits += 1;
break;
}
}
//
// No match. If the entry is expired, put it on the free list.
//
ExpiredEntry = pListEntry;
pListEntry = pListEntry->Flink;
if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
RemoveEntryList(ExpiredEntry);
InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry expired %wZ\n", &NameCache->Name ));
}
NameCache = NULL;
}
//
// If we found something pull it off the active list and give it to caller.
//
if (NameCache != NULL) {
RemoveEntryList(pListEntry);
}
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
if (NameCache != NULL) {
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry found %wZ\n", &NameCache->Name ));
}
return NameCache;
}
RX_NC_CHECK_STATUS
RxNameCacheCheckEntry (
IN PNAME_CACHE NameCache,
IN ULONG MRxContext
)
/*++
Routine Description:
This routine checks a name cache entry for validity. A valid entry
means that the lifetime has not expired and the MRxContext passes
the equality check.
Arguments:
NameCache - pointer to NAME_CACHE struct to check.
MRxContext - A ULONG worth of mini-rdr supplied context for
equality checking when making a valid entry check.
Return Value:
RX_NC_CHECK_STATUS: RX_NC_SUCCESS - The entry is valid
RX_NC_TIME_EXPIRED - The Lifetime on the entry expired
RX_NC_MRXCTX_FAIL - The MRxContext equality test failed
--*/
{
LARGE_INTEGER CurrentTime;
PAGED_CODE();
//
// Check for Mini-rdr context equality.
//
if (NameCache->Context != MRxContext) {
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: MRxContext_Fail %08lx,%08lx %wZ\n",
NameCache->Context,
MRxContext,
&NameCache->Name ));
return RX_NC_MRXCTX_FAIL;
}
//
// Check for lifetime expired.
//
KeQueryTickCount( &CurrentTime );
if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Expired %wZ\n", &NameCache->Name ));
return RX_NC_TIME_EXPIRED;
}
RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Success %wZ\n", &NameCache->Name ));
return RX_NC_SUCCESS;
}
VOID
RxNameCacheExpireEntry(
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PNAME_CACHE NameCache
)
/*++
Routine Description:
This routine puts the entry on the free list.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
activate the entry.
NameCache - pointer to NAME_CACHE struct to activate.
Return Value:
None.
Assumes:
The name cache entry is not on either the free or active list.
--*/
{
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntry: %wZ\n", &NameCache->Name ));
//
// Put the entry on free list for recycle.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
//
// Update stats.
//
NameCacheCtl->NumberActivates -= 1;
return;
}
VOID
RxNameCacheExpireEntryWithShortName (
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PUNICODE_STRING Name
)
/*++
Routine Description:
This routine expires all the name cache whose name prefix matches the given short file name.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
Name - A pointer to the unicode name string to scan for.
Return Value:
PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
Side Effects:
As the active list is scanned any non-matching entries that have expired are
put on the free list.
--*/
{
PNAME_CACHE NameCache = NULL;
PLIST_ENTRY pListEntry;
PLIST_ENTRY ExpiredEntry;
ULONG HashValue;
LARGE_INTEGER CurrentTime;
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
KeQueryTickCount( &CurrentTime );
NameCacheCtl->NumberChecks += 1;
//
// Get the lock and scan the active list.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
pListEntry = NameCacheCtl->ActiveList.Flink;
while (pListEntry != &NameCacheCtl->ActiveList) {
USHORT SavedNameLength;
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
ExpiredEntry = pListEntry;
pListEntry = pListEntry->Flink;
//
// Do initial match on the hash value and the length. Then do full string.
//
if (Name->Length <= NameCache->Name.Length) {
SavedNameLength = NameCache->Name.Length;
NameCache->Name.Length = Name->Length;
if (Name->Length == 0 ||
RtlEqualUnicodeString(
Name,
&NameCache->Name,
NameCache->CaseInsensitive) ) {
//
// Found a match.
//
RemoveEntryList(ExpiredEntry);
InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntryWithShortName: Entry expired %wZ\n", &NameCache->Name ));
continue;
}
NameCache->Name.Length = SavedNameLength;
}
}
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
}
VOID
RxNameCacheActivateEntry (
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PNAME_CACHE NameCache,
IN ULONG LifeTime,
IN ULONG MRxContext
)
/*++
Routine Description:
This routine takes a name cache entry and updates the expiration time and
the mini rdr context. It then puts the entry on the active list.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
activate the entry.
NameCache - pointer to NAME_CACHE struct to activate.
LifeTime - The valid lifetime of the cache entry (in seconds).
A lifetime of zero means leave current value unchanged.
This is for reactivations after a match where you
want the original lifetime preserved.
MRxContext - A ULONG worth of mini-rdr supplied context for
equality checking when making a valid entry check.
An MRxContext of zero means leave current value unchanged.
This is for reactivations after a match where you
want the original MRxContext preserved.
Return Value:
None.
Assumes:
The name cache entry is not on either the free or active list.
--*/
{
LARGE_INTEGER CurrentTime;
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("RxNameCacheActivateEntry: %wZ\n", &NameCache->Name ));
//
// Set new expiration time on the entry and save the mini-rdr context.
// A lifetime of zero or a MRxContext of zero implies leave value unchanged.
//
if (LifeTime != 0) {
KeQueryTickCount( &CurrentTime );
NameCache->ExpireTime.QuadPart = CurrentTime.QuadPart +
(LONGLONG) ((LifeTime * 10*1000*1000) / KeQueryTimeIncrement());
}
if (MRxContext != 0) {
NameCache->Context = MRxContext;
}
//
// Put the entry on active list.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
InsertHeadList(&NameCacheCtl->ActiveList, &NameCache->Link);
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
//
// Update stats.
//
NameCacheCtl->NumberActivates += 1;
return;
}
VOID
RxNameCacheFreeEntry (
IN PNAME_CACHE_CONTROL NameCacheCtl,
IN PNAME_CACHE NameCache
)
/*++
Routine Description:
This routine releases the storage for a name cache entry and decrements the
count of name cache entries for this name cache.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
NameCache - pointer to the NAME_CACHE struct to free.
Return Value:
None.
Assumes:
The name cache entry is not on either the free or active list.
--*/
{
PAGED_CODE();
RxDbgTrace( 0, Dbg, ("RxNameCacheFreeEntry: %wZ\n", &NameCache->Name ));
//
// Release storage for name
//
if (NameCache->Name.Buffer != NULL) {
RxFreePool(NameCache->Name.Buffer);
}
//
// Release storage for NAME_CACHE entry (includes context ext., if any)
//
RxFreePool(NameCache);
InterlockedDecrement(&NameCacheCtl->EntryCount);
return;
}
VOID
RxNameCacheFinalize (
IN PNAME_CACHE_CONTROL NameCacheCtl
)
/*++
Routine Description:
This routine releases the storage for all the name cache entries.
Arguments:
NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
Return Value:
None.
--*/
{
PNAME_CACHE NameCache;
PLIST_ENTRY pListEntry;
PAGED_CODE();
//
// Get the lock and remove entries from the active list.
//
ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
while (!IsListEmpty(&NameCacheCtl->ActiveList)) {
pListEntry = RemoveHeadList(&NameCacheCtl->ActiveList);
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
RxNameCacheFreeEntry(NameCacheCtl, NameCache);
}
//
// scan free list and remove entries.
//
while (!IsListEmpty(&NameCacheCtl->FreeList)) {
pListEntry = RemoveHeadList(&NameCacheCtl->FreeList);
NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
RxNameCacheFreeEntry(NameCacheCtl, NameCache);
}
ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
//
// At this point the entry count should be zero. If not then there is
// a memory leak since someone didn't call free.
//
ASSERT(NameCacheCtl->EntryCount == 0);
return;
}