Windows2003-3790/drivers/ksfilter/ks/alloc.c
2020-09-30 16:53:55 +02:00

1545 lines
42 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
Alloc.c
Abstract:
This module contains the helper functions for allocators.
Author:
Bryan A. Woodruff (bryanw) 13-Sep-1996
--*/
//
// Idealy we should be a wdm driver and include wdm.h. We should try to make it.
// But until then, we include ntddk.h which has these functions redefined for whistler
// ExFreeToNPagedLookasideList
// ExFreeToPagedLookasideList
// ExAllocateFromNPagedLookasideList
// ExAllocateFromPagedLookasideList
// to use non-Ex versions of
// InterlockedPushEntrySList
// InterlockedPopEntryList
// which are not available in down level OSes. Here we deine win9x_ks so we
// include wdm.h to keep backward compatibility
//
#define USE_WDM_H
#include "ksp.h"
#define KSSIGNATURE_DEFAULT_ALLOCATOR 'adSK'
#define KSSIGNATURE_DEFAULT_ALLOCATORINST 'iaSK'
//
// The assumption is that all Paged pool types have the lowest bit set.
//
#define BASE_POOL_TYPE 1
typedef struct {
//
// This pointer to the dispatch table is used in the common
// dispatch routines to route the IRP to the appropriate
// handlers. This structure is referenced by the device driver
// with IoGetCurrentIrpStackLocation(Irp)->FsContext
//
KSOBJECT_HEADER Header;
ULONG AllocatedFrames;
KSPIN_LOCK EventLock;
LIST_ENTRY EventQueue;
LIST_ENTRY WaiterQueue;
KSALLOCATOR_FRAMING Framing;
KSEVENTDATA EventData;
KSPIN_LOCK WaiterLock;
WORK_QUEUE_ITEM FreeWorkItem;
PFNKSDEFAULTALLOCATE DefaultAllocate;
PFNKSDEFAULTFREE DefaultFree;
PFNKSDELETEALLOCATOR DeleteAllocator;
PVOID Context;
KEVENT Event;
LONG ReferenceCount;
BOOL ClosingAllocator;
#ifdef _WIN64
//
// Due to the fact that we're placing an NPAGED_LOOKASIDE_LIST after
// this for certain types of allocators and the alignment of that
// structure must be 16 bytes on Win64, pad this data structure length to
// 16-byte alignment.
//
ULONG64 Alignment;
#endif // _WIN64
} KSDEFAULTALLOCATOR_INSTANCEHDR, *PKSDEFAULTALLOCATOR_INSTANCEHDR;
#ifdef ALLOC_PRAGMA
NTSTATUS
DefAllocatorClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
DefAllocatorGetFunctionTable(
IN PIRP Irp,
IN PKSPROPERTY Property,
OUT PKSSTREAMALLOCATOR_FUNCTIONTABLE FunctionTable
);
NTSTATUS
DefAllocatorGetStatus(
IN PIRP Irp,
IN PKSPROPERTY Property,
OUT PKSSTREAMALLOCATOR_STATUS Status
);
NTSTATUS
DefAllocatorIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
FreeWorker(
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance
);
NTSTATUS
MethodAlloc(
IN PIRP Irp,
IN PKSMETHOD Method,
OUT PVOID* Data
);
NTSTATUS
MethodFree(
IN PIRP Irp,
IN PKSMETHOD Method,
IN OUT PVOID Data
);
#pragma alloc_text(PAGE, KsCreateAllocator)
#pragma alloc_text(PAGE, KsValidateAllocatorCreateRequest)
#pragma alloc_text(PAGE, KsValidateAllocatorFramingEx)
#pragma alloc_text(PAGE, KsCreateDefaultAllocator)
#pragma alloc_text(PAGE, KsCreateDefaultAllocatorEx)
#pragma alloc_text(PAGE, DefAllocatorIoControl)
#pragma alloc_text(PAGE, DefAllocatorClose)
#pragma alloc_text(PAGE, DefAllocatorGetFunctionTable)
#pragma alloc_text(PAGE, DefAllocatorGetStatus)
#pragma alloc_text(PAGE, FreeWorker)
#pragma alloc_text(PAGE, MethodAlloc)
#pragma alloc_text(PAGE, MethodFree)
#endif
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif // ALLOC_DATA_PRAGMA
static const WCHAR AllocatorString[] = KSSTRING_Allocator;
static DEFINE_KSDISPATCH_TABLE(
DefAllocatorDispatchTable,
DefAllocatorIoControl,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
KsDispatchInvalidDeviceRequest,
DefAllocatorClose,
KsDispatchQuerySecurity,
KsDispatchSetSecurity,
KsDispatchFastIoDeviceControlFailure,
KsDispatchFastReadFailure,
KsDispatchFastWriteFailure);
static DEFINE_KSMETHOD_ALLOCATORSET(
StreamAllocatorMethodHandlers,
MethodAlloc,
MethodFree);
static DEFINE_KSMETHOD_SET_TABLE( DefAllocatorMethodTable )
{
DEFINE_KSMETHOD_SET( &KSMETHODSETID_StreamAllocator,
SIZEOF_ARRAY( StreamAllocatorMethodHandlers ),
StreamAllocatorMethodHandlers,
0, NULL )
};
static DEFINE_KSPROPERTY_ALLOCATORSET(
DefAllocatorPropertyHandlers,
DefAllocatorGetFunctionTable,
DefAllocatorGetStatus );
static DEFINE_KSPROPERTY_SET_TABLE( DefAllocatorPropertyTable )
{
DEFINE_KSPROPERTY_SET( &KSPROPSETID_StreamAllocator,
SIZEOF_ARRAY( DefAllocatorPropertyHandlers ),
DefAllocatorPropertyHandlers,
0, NULL )
};
static DEFINE_KSEVENT_TABLE( DefAllocatorEventTable )
{
DEFINE_KSEVENT_ITEM( KSEVENT_STREAMALLOCATOR_INTERNAL_FREEFRAME,
sizeof( KSEVENTDATA ),
0,
NULL,
NULL,
NULL ),
DEFINE_KSEVENT_ITEM( KSEVENT_STREAMALLOCATOR_FREEFRAME,
sizeof( KSEVENTDATA ),
0,
NULL,
NULL,
NULL )
};
static DEFINE_KSEVENT_SET_TABLE( DefAllocatorEventSetTable )
{
DEFINE_KSEVENT_SET( &KSEVENTSETID_StreamAllocator,
SIZEOF_ARRAY( DefAllocatorEventTable ),
DefAllocatorEventTable )
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif // ALLOC_DATA_PRAGMA
KSDDKAPI
NTSTATUS
NTAPI
KsCreateAllocator(
IN HANDLE ConnectionHandle,
IN PKSALLOCATOR_FRAMING AllocatorFraming,
OUT PHANDLE AllocatorHandle
)
/*++
Routine Description:
This function creates a handle to an allocator for the given sink
connection handle. There are two versions of this function, one
for user-mode clients and one for kernel-mode clients. This
function may only be called at PASSIVE_LEVEL for kernel mode
clients.
Arguments:
ConnectionHandle -
Contains the handle to the sink connection on which to create
the allocator.
AllocatorFraming -
Specified framing for the allocator.
AllocatorHandle -
Place in which to put the allocator handle.
Return Value:
Returns STATUS_SUCCESS, else an error on allocator creation failure.
--*/
{
PAGED_CODE();
return KsiCreateObjectType( ConnectionHandle,
(PWCHAR)AllocatorString,
AllocatorFraming,
sizeof(*AllocatorFraming),
GENERIC_READ,
AllocatorHandle );
}
KSDDKAPI
NTSTATUS
NTAPI
KsValidateAllocatorCreateRequest(
IN PIRP Irp,
OUT PKSALLOCATOR_FRAMING* AllocatorFraming
)
/*++
Routine Description:
Validates the allocator creation request and returns the create
structure associated with the request.
Arguments:
Irp -
Contains the IRP with the allocator create request being handled.
AllocatorFraming -
Pointer to a pointer to an allocator create structure pointer in
which to put the pointer to the framing structure supplied with
the request.
Return Value:
Returns STATUS_SUCCESS, else the allocator request is not valid.
--*/
{
NTSTATUS Status;
ULONG CreateParameterLength;
PAGED_CODE();
//
// This validates the incoming address and captures the request block.
// This pointer will be freed automatically in IRP completion.
//
CreateParameterLength = sizeof(**AllocatorFraming);
if (!NT_SUCCESS(Status =
KsiCopyCreateParameter(
Irp,
&CreateParameterLength,
AllocatorFraming))) {
return Status;
}
//
// Validate the captured request block, then pass back an address to the
// captured buffer.
//
if ((*AllocatorFraming)->OptionsFlags & ~KSALLOCATOR_OPTIONF_VALID) {
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
KSDDKAPI
NTSTATUS
NTAPI
KsValidateAllocatorFramingEx(
IN PKSALLOCATOR_FRAMING_EX Framing,
IN ULONG BufferSize,
IN const KSALLOCATOR_FRAMING_EX *PinFraming
)
/*++
Routine Description:
Validates allocator framing submitted in a 'set' of the property
KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX.
Arguments:
Framing -
Contains the framing structure to validate.
BufferSize -
Contains the size of the buffer containing the framing structure.
PinFraming -
Contains the framing structure exposed by the pin. This is the
structure that is returned when a 'get' is performed on the
same property.
Return Value:
Returns STATUS_SUCCESS, else an error.
--*/
{
if ((BufferSize >= sizeof(*PinFraming)) &&
(Framing->FramingItem[0].Flags & KSALLOCATOR_FLAG_PARTIAL_READ_SUPPORT) &&
(Framing->OutputCompression.RatioNumerator < (ULONG) -1) &&
(Framing->OutputCompression.RatioNumerator > Framing->OutputCompression.RatioDenominator) &&
Framing->OutputCompression.RatioDenominator) {
return STATUS_SUCCESS;
}
return STATUS_INVALID_DEVICE_REQUEST;
}
PVOID
DefAllocatorAlloc(
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Alignment
)
/*++
Routine Description:
Performs the actual memory allocation from the specified memory pool for
the default allocator. May be called at DISPATCH_LEVEL, must be in a
non-paged section! Allocates a piece of memory large enough to compensate
for any adjustment for alignment.
Arguments:
PoolType -
Type of pool to allocate from.
NumberOfBytes -
Number of bytes to allocate.
Alignment -
Specifies the minimum alignment mask each allocation must have. This
must correspond to a power of 2 alignment.
Return Value:
Returns a pointer to the allocated memory, offset by the header stored in
each allocation.
--*/
{
PVOID Buffer;
ULONG Pad;
//
// The minimum alignment is FILE_QUAD_ALIGNMENT, so always bump up the
// request.
//
if (Alignment < FILE_QUAD_ALIGNMENT) {
Alignment = FILE_QUAD_ALIGNMENT;
}
//
// If the alignment is specified to be on a page boundary, then
// allocate at least a page to force this alignment.
//
if (Alignment == (PAGE_SIZE - 1)) {
Buffer =
ExAllocatePoolWithTag(
PoolType,
max( PAGE_SIZE, NumberOfBytes ),
KSSIGNATURE_DEFAULT_ALLOCATOR );
ASSERT( ((ULONG_PTR) Buffer & (PAGE_SIZE - 1)) == 0 );
return Buffer;
}
//
// The returned block has a header which contains an inclusive padding
// count. However, the address returned must also conform to the specified
// alignment, so add the size of both items to the allocation size so that
// there will always be enough room.
//
NumberOfBytes = NumberOfBytes + Alignment + sizeof( Pad );
Buffer = ExAllocatePoolWithTag( PoolType,
NumberOfBytes,
KSSIGNATURE_DEFAULT_ALLOCATOR );
if (Buffer) {
//
// Pool allocation always returns FILE_QUAD_ALIGNMENT pointers, but ensure
// that the pointer is at least at an alignment that the ULONG padding
// can be used without causing unaligned access faults.
//
ASSERT( !((ULONG_PTR) Buffer & FILE_LONG_ALIGNMENT) );
//
// The padding is how much you need to back up to get to the real start
// of the buffer. The returned address is padded to line up with the
// Alignment requirement.
//
Pad = (ULONG)((((ULONG_PTR) Buffer + sizeof( Pad ) + Alignment) & ~(ULONG_PTR)Alignment) - (ULONG_PTR) Buffer);
ASSERT( Pad >= sizeof( Pad ) );
//
// Stuff the inclusive padding size just before the beginning of the
// returned address so that a Free will know how much total to back up
// to get the real start of the buffer.
//
(PUCHAR) Buffer += Pad;
*((PULONG)Buffer - 1) = Pad;
}
return Buffer;
}
VOID
DefAllocatorFree(
PVOID Buffer
)
/*++
Routine Description:
Frees memory previously allocated with the default allocator. Assumes that
such memory has indeed been allocated by the default allocator, as it also
retrieves the padding from the header on the memory block. May be called at
DISPATCH_LEVEL, must be in a non-paged section!
Arguments:
Buffer -
Contains the memory block to free.
Return Value:
Nothing.
--*/
{
ULONG_PTR Pad;
//
// If this is page-aligned, the padding size is zero.
//
if ((ULONG_PTR) Buffer & (PAGE_SIZE - 1)) {
//
// Just previous to the memory pointer being freed is the inclusive padding
// count, then any padding itself. Since the allocation itself is always at
// least FILE_LONG_ALIGNMENT, then the padding count will be aligned enough
// to extract the value directly.
//
Pad = *((PULONG)Buffer - 1);
} else {
Pad = 0;
}
ExFreePool( (PUCHAR)Buffer - Pad );
}
NTSTATUS
iAlloc(
PFILE_OBJECT FileObject,
PVOID *Buffer
)
/*++
Routine Description:
The allocator function which is used by the allocation method, and by the
direct function table allocation calls. This uses the type of lookaside
list set up in the creation of the Allocator to return a piece of memory.
This may cause an actual piece of memory to be allocated, or just return
a previously allocated piece of memory.
Arguments:
FileObject -
This is the file object which was returned during the creation of this
Allocator.
Return Value:
Returns the requested memory, or NULL if no more frames in the list are
available.
--*/
{
NTSTATUS Status;
PKSDEFAULTALLOCATOR_INSTANCEHDR Allocator;
//
// N.B.:
//
// All both types of list allocations are handled and specifically
// dispatched by the function pointers initialized during the allocator
// creation.
//
Allocator = (PKSDEFAULTALLOCATOR_INSTANCEHDR) FileObject->FsContext;
//
// This enforces a maximum number of allocations which can be performed on
// this list.
//
Status = STATUS_SUCCESS;
if ((ULONG) InterlockedIncrement( (PLONG)&Allocator->AllocatedFrames ) <=
Allocator->Framing.Frames) {
*Buffer = Allocator->DefaultAllocate( Allocator->Context );
if (!*Buffer) {
//
// We ran out of pool.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
//
// We have no frames available, the buffer pointer is NULL,
// but return STATUS_NO_MORE_ENTRIES to allow a wait to occur.
//
*Buffer = NULL;
Status = STATUS_NO_MORE_ENTRIES;
}
//
// If a frame could not be allocated, decrement the total frame count.
//
if (!*Buffer) {
InterlockedDecrement( (PLONG)&Allocator->AllocatedFrames );
}
return Status;
}
VOID
iFree(
PFILE_OBJECT FileObject,
PVOID Buffer
)
/*++
Routine Description:
The free function which is used by the free method, and within the function
used in the direct function table free calls. This uses the type of
list set up in the creation of the Allocator to free the piece of
memory. If using internal allocators, the memory block is placed onto
the lookaside list.
Arguments:
FileObject -
This is the file object which was returned during the creation of this
Allocator.
Buffer -
The buffer to free which was previously allocated from this list.
Return Value:
Nothing.
--*/
{
PKSDEFAULTALLOCATOR_INSTANCEHDR Allocator;
Allocator =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) FileObject->FsContext;
Allocator->DefaultFree( Allocator->Context, Buffer );
InterlockedDecrement( (PLONG)&Allocator->AllocatedFrames );
// Generate notification of free frame to any clients which may be waiting.
KsGenerateEventList( NULL,
KSEVENT_STREAMALLOCATOR_FREEFRAME,
&Allocator->EventQueue,
KSEVENTS_SPINLOCK,
&Allocator->EventLock );
}
VOID
iFreeAndStartWorker(
PFILE_OBJECT FileObject,
PVOID Buffer
)
/*++
Routine Description:
This function is used in the direct function table free calls. It calls the
internal free function, and notifies the worker thread that a new free frame
is available for use. This allows any waiters on frames to be notified of
the free frame, either by completing an outstanding IRP with an allocation
request pending, or by generating an event for clients using the direct
function table allocation scheme.
Arguments:
FileObject -
This is the file object which was returned during the creation of this
Allocator.
Buffer -
The buffer to free which was previously allocated from this list.
Return Value:
Nothing.
--*/
{
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
DefAllocatorInstance =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) FileObject->FsContext;
iFree( FileObject, Buffer );
// Generate notification of free frame for any pending allocation requests.
// A background worker thread attempts to fulfill any requests.
KsGenerateEventList( NULL,
KSEVENT_STREAMALLOCATOR_INTERNAL_FREEFRAME,
&DefAllocatorInstance->EventQueue,
KSEVENTS_SPINLOCK,
&DefAllocatorInstance->EventLock );
}
KSDDKAPI
NTSTATUS
NTAPI
KsCreateDefaultAllocator(
IN PIRP Irp
)
/*++
Routine Description:
Given a validated IRP_MJ_CREATE request, creates a default allocator
which uses the specified memory pool and associates the
IoGetCurrentIrpStackLocation(pIrp)->FileObject with this allocator
using an internal dispatch table (KSDISPATCH_TABLE). Assumes that the
KSCREATE_ITEM_IRP_STORAGE(Irp) points to the create item for this
allocator, and assigns a pointer to it in the FsContext. This is used for
any security descriptor queries or changes.
Arguments:
Irp -
Contains the IRP with the allocator create request being handled.
Return Value:
Returns STATUS_SUCCESS, else an error on default allocator creation
failure. Does not complete the Irp or set the status in the Irp.
--*/
{
PAGED_CODE();
return KsCreateDefaultAllocatorEx(Irp, NULL, NULL, NULL, NULL, NULL);
}
KSDDKAPI
NTSTATUS
NTAPI
KsCreateDefaultAllocatorEx(
IN PIRP Irp,
IN PVOID InitializeContext OPTIONAL,
IN PFNKSDEFAULTALLOCATE DefaultAllocate OPTIONAL,
IN PFNKSDEFAULTFREE DefaultFree OPTIONAL,
IN PFNKSINITIALIZEALLOCATOR InitializeAllocator OPTIONAL,
IN PFNKSDELETEALLOCATOR DeleteAllocator OPTIONAL
)
/*++
Routine Description:
Given a validated IRP_MJ_CREATE request, creates a default allocator
which uses the specified memory pool and associates the
IoGetCurrentIrpStackLocation(pIrp)->FileObject with this allocator
using an internal dispatch table (KSDISPATCH_TABLE). Assumes that the
KSCREATE_ITEM_IRP_STORAGE(Irp) points to the create item for this
allocator, and assigns a pointer to it in the FsContext. This is used for
any security descriptor queries or changes.
Arguments:
Irp -
Contains the IRP with the allocator create request being handled.
InitializeContext -
Optionally contains a context to use with an external allocator.
This is only used as the initialization context to the optional
InitializeAllocator callback when creating an allocator context.
The parameter is not otherwised used. If an external allocator
is not provided, this parameter must be set to NULL.
DefaultAllocate -
Optionally contains an external allocate function which is used
in place of the default pool allocation. If this is NULL, default
allocation is used.
DefaultFree -
Optionally contains an external free function which is used in
place of the default pool allocation. If an external allocator
is not provided, this parameter must be set to NULL.
InitializeAllocator -
Optionally contains an external allocator initialization function
to which the InitializeContext parameter is passed. This function
is expected to return an allocator context based on the allocator
framing. If an external allocator is not provided, this parameter
must be set to NULL.
DeleteAllocator -
Optionally contains an external allocator delete function which
is used for external allocators. If an external allocator is not
provided, this parameter must be set to NULL.
Return Value:
Returns STATUS_SUCCESS, else an error on default allocator creation
failure. Does not complete the Irp or set the status in the Irp.
--*/
{
NTSTATUS Status;
KSEVENT Event;
PIO_STACK_LOCATION irpSp;
PKSALLOCATOR_FRAMING AllocatorFraming;
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
ULONG BytesReturned;
ULONG AllocatorSize;
enum {
ALLOCATOR_TYPE_NONPAGED,
ALLOCATOR_TYPE_PAGED,
ALLOCATOR_TYPE_EXTERNAL
} AllocatorType;
PAGED_CODE();
//
// Retrieve the captured Allocator create parameter.
//
Status = KsValidateAllocatorCreateRequest( Irp,
&AllocatorFraming );
if (!NT_SUCCESS( Status )) {
return Status;
}
if (AllocatorFraming->FileAlignment > (PAGE_SIZE - 1)) {
return STATUS_INVALID_PARAMETER;
}
//
// The major distinction is External, PagedPool, or NonPagedPool. All pool
// types have the single bit distinction, so this is the only bit that is
// tested when determining what type of list to allocate when not Custom.
//
AllocatorSize = sizeof( KSDEFAULTALLOCATOR_INSTANCEHDR );
if (DefaultAllocate) {
ASSERT(InitializeContext);
ASSERT(DefaultFree);
ASSERT(InitializeAllocator);
ASSERT(DeleteAllocator);
AllocatorType = ALLOCATOR_TYPE_EXTERNAL;
} else if ((AllocatorFraming->PoolType & BASE_POOL_TYPE) == NonPagedPool) {
ASSERT(!InitializeContext);
ASSERT(!DefaultFree);
ASSERT(!InitializeAllocator);
ASSERT(!DeleteAllocator);
AllocatorSize += sizeof( NPAGED_LOOKASIDE_LIST );
AllocatorType = ALLOCATOR_TYPE_NONPAGED;
} else {
ASSERT(!InitializeContext);
ASSERT(!DefaultFree);
ASSERT(!InitializeAllocator);
ASSERT(!DeleteAllocator);
AllocatorSize += sizeof( PAGED_LOOKASIDE_LIST );
AllocatorType = ALLOCATOR_TYPE_PAGED;
}
DefAllocatorInstance = ExAllocatePoolWithTag(
NonPagedPool,
AllocatorSize,
KSSIGNATURE_DEFAULT_ALLOCATORINST);
if (!DefAllocatorInstance) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Allocate an object header for routing Irp's.
//
Status = KsAllocateObjectHeader( &DefAllocatorInstance->Header,
0,
NULL,
Irp,
(PKSDISPATCH_TABLE) &DefAllocatorDispatchTable );
if (!NT_SUCCESS(Status)) {
ExFreePool( DefAllocatorInstance );
return Status;
}
DefAllocatorInstance->Framing = *AllocatorFraming;
irpSp = IoGetCurrentIrpStackLocation( Irp );
irpSp->FileObject->FsContext = DefAllocatorInstance;
InitializeListHead( &DefAllocatorInstance->EventQueue );
InitializeListHead( &DefAllocatorInstance->WaiterQueue );
KeInitializeSpinLock( &DefAllocatorInstance->EventLock );
KeInitializeSpinLock( &DefAllocatorInstance->WaiterLock );
DefAllocatorInstance->AllocatedFrames = 0;
// NOTE: The tag parameter is overridden with the framing alignment.
// The Alloc function forces the standard default allocator signature
// as the tag parameter to ExAllocatePoolWithTag().
switch (AllocatorType) {
case ALLOCATOR_TYPE_NONPAGED:
DefAllocatorInstance->Context = DefAllocatorInstance + 1;
ExInitializeNPagedLookasideList(
(PNPAGED_LOOKASIDE_LIST)DefAllocatorInstance->Context,
DefAllocatorAlloc,
DefAllocatorFree,
0,
AllocatorFraming->FrameSize,
AllocatorFraming->FileAlignment,
(USHORT) AllocatorFraming->Frames );
DefAllocatorInstance->DefaultAllocate =
(PFNKSDEFAULTALLOCATE) ExAllocateFromNPagedLookasideList;
DefAllocatorInstance->DefaultFree =
(PFNKSDEFAULTFREE) ExFreeToNPagedLookasideList;
DefAllocatorInstance->DeleteAllocator =
(PFNKSDELETEALLOCATOR) ExDeleteNPagedLookasideList;
break;
case ALLOCATOR_TYPE_PAGED:
DefAllocatorInstance->Context = DefAllocatorInstance + 1;
ExInitializePagedLookasideList(
(PPAGED_LOOKASIDE_LIST)DefAllocatorInstance->Context,
DefAllocatorAlloc,
DefAllocatorFree,
0,
AllocatorFraming->FrameSize,
AllocatorFraming->FileAlignment,
(USHORT) AllocatorFraming->Frames );
DefAllocatorInstance->DefaultAllocate =
(PFNKSDEFAULTALLOCATE) ExAllocateFromPagedLookasideList;
DefAllocatorInstance->DefaultFree =
(PFNKSDEFAULTFREE) ExFreeToPagedLookasideList;
DefAllocatorInstance->DeleteAllocator =
(PFNKSDELETEALLOCATOR) ExDeletePagedLookasideList;
break;
default://ALLOCATOR_TYPE_EXTERNAL
Status = InitializeAllocator(
InitializeContext,
AllocatorFraming,
&DefAllocatorInstance->Context);
if (!NT_SUCCESS(Status)) {
KsFreeObjectHeader(DefAllocatorInstance->Header);
ExFreePool(DefAllocatorInstance);
return Status;
}
DefAllocatorInstance->DefaultAllocate = DefaultAllocate;
DefAllocatorInstance->DefaultFree = DefaultFree;
DefAllocatorInstance->DeleteAllocator = DeleteAllocator;
break;
}
// Enable the internal event used to kick off a work item whenever
// an element is freed up. This allows the DISPATCH_LEVEL calls to
// eventually service any pending allocator IRP's.
ExInitializeWorkItem(
&DefAllocatorInstance->FreeWorkItem,
(PWORKER_THREAD_ROUTINE) FreeWorker,
(PVOID) DefAllocatorInstance );
DefAllocatorInstance->FreeWorkItem.List.Blink = NULL;
DefAllocatorInstance->EventData.WorkItem.Reserved = 0;
DefAllocatorInstance->EventData.WorkItem.WorkQueueItem =
&DefAllocatorInstance->FreeWorkItem;
DefAllocatorInstance->EventData.WorkItem.WorkQueueType = CriticalWorkQueue;
DefAllocatorInstance->EventData.NotificationType = KSEVENTF_WORKITEM;
KeInitializeEvent(&DefAllocatorInstance->Event, NotificationEvent, FALSE);
DefAllocatorInstance->ReferenceCount = 0;
DefAllocatorInstance->ClosingAllocator = FALSE;
Event.Set = KSEVENTSETID_StreamAllocator;
Event.Id = KSEVENT_STREAMALLOCATOR_INTERNAL_FREEFRAME;
Event.Flags = KSEVENT_TYPE_ENABLE;
Status = KsSynchronousIoControlDevice(
irpSp->FileObject,
KernelMode,
IOCTL_KS_ENABLE_EVENT,
&Event,
sizeof( Event ),
&DefAllocatorInstance->EventData,
sizeof( DefAllocatorInstance->EventData ),
&BytesReturned );
ASSERT(Status != STATUS_PENDING);
if (!NT_SUCCESS(Status)) {
DefAllocatorInstance->DeleteAllocator(DefAllocatorInstance->Context);
KsFreeObjectHeader(DefAllocatorInstance->Header);
ExFreePool(DefAllocatorInstance);
}
return Status;
}
NTSTATUS
DefAllocatorIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The IRP handler for IRP_MJ_DEVICE_CONTROL for the default Allocator. Handles
the properties, methods, and events supported by this implementation.
Arguments:
DeviceObject -
The device object to which the Allocator is attached. This is not used.
Irp -
The specific device control IRP to be processed.
Return Value:
Returns the status of the processing, which may be pending.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION irpSp;
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation( Irp );
DefAllocatorInstance =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) irpSp->FileObject->FsContext;
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_KS_PROPERTY:
Status =
KsPropertyHandler( Irp,
SIZEOF_ARRAY( DefAllocatorPropertyTable ),
(PKSPROPERTY_SET) DefAllocatorPropertyTable );
break;
case IOCTL_KS_METHOD:
Status =
KsMethodHandler( Irp,
SIZEOF_ARRAY( DefAllocatorMethodTable ),
(PKSMETHOD_SET) DefAllocatorMethodTable );
break;
case IOCTL_KS_ENABLE_EVENT:
Status = KsEnableEvent( Irp,
SIZEOF_ARRAY( DefAllocatorEventSetTable ),
(PKSEVENT_SET) DefAllocatorEventSetTable,
&DefAllocatorInstance->EventQueue,
KSEVENTS_SPINLOCK,
&DefAllocatorInstance->EventLock );
break;
case IOCTL_KS_DISABLE_EVENT:
Status =
KsDisableEvent( Irp,
&DefAllocatorInstance->EventQueue,
KSEVENTS_SPINLOCK,
&DefAllocatorInstance->EventLock );
break;
default:
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
//
// Allocation requests may be returned STATUS_PENDING.
//
Irp->IoStatus.Status = Status;
if (Status != STATUS_PENDING) {
IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
return Status;
}
NTSTATUS
DefAllocatorClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The IRP handler for IRP_MJ_CLOSE for the default Allocator. Cleans up the
lookaside list, and instance data.
Arguments:
DeviceObject -
The device object to which the Allocator is attached. This is not used.
Irp -
The specific close IRP to be processed.
Return Value:
Returns STATUS_SUCCESS.
--*/
{
PIO_STACK_LOCATION irpSp;
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation( Irp );
DefAllocatorInstance =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) irpSp->FileObject->FsContext;
//
// Free any outstanding events which have been enabled.
//
KsFreeEventList( irpSp->FileObject,
&DefAllocatorInstance->EventQueue,
KSEVENTS_SPINLOCK,
&DefAllocatorInstance->EventLock );
//
// If a worker is running, or is queued to run, then wait for
// completion.
//
DefAllocatorInstance->ClosingAllocator = TRUE;
if (DefAllocatorInstance->FreeWorkItem.List.Blink ||
DefAllocatorInstance->ReferenceCount) {
KeWaitForSingleObject(
&DefAllocatorInstance->Event,
Executive,
KernelMode,
FALSE,
NULL);
}
//
// Free the object header that was allocated on Create, and the FsContext.
//
DefAllocatorInstance->DeleteAllocator(DefAllocatorInstance->Context);
KsFreeObjectHeader(DefAllocatorInstance->Header);
ExFreePool( DefAllocatorInstance );
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS
iMethodAlloc(
IN PIRP Irp,
IN PKSMETHOD Method,
OUT PVOID* Data
)
/*++
Routine Description:
Attempts to allocate memory using the internal direct function call
allocation routine. Callers to this function take action of failure
to allocate a frame by placing the request on a queue.
Arguments:
Irp -
The specific alloc method IRP to be processed.
Method -
Points to the method identifier parameter.
Data -
Points to the place in which to put the returned pointer to the memory
block allocated.
Return Value:
If memory is allocated, sets the pointer and return size in the IRP, and
returns STATUS_SUCCESS. Else returns STATUS_INSUFFICIENT_RESOURCES. Does
not complete the IRP.
--*/
{
NTSTATUS Status;
PIO_STACK_LOCATION irpSp;
PVOID Buffer;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation( Irp );
Status = iAlloc( irpSp->FileObject, &Buffer );
//
// The Irp is only completed if the frame allocation succeeds, else it
// is queued. So fill in the return size (a pointer to the frame is
// returned) on success.
//
//
// Note that iAlloc will return the proper status if we are unable
// to allocate a frame because of pool resources.
//
if (Buffer) {
*Data = Buffer;
Irp->IoStatus.Information = sizeof( *Data );
}
return Status;
}
NTSTATUS
MethodAlloc(
IN PIRP Irp,
IN PKSMETHOD Method,
OUT PVOID* Data
)
/*++
Routine Description:
This function is the method handler for KSMETHOD_STREAMALLOCATOR_ALLOC, and
calls the internal allocation method. If the allocation fails, this function
will make the IRP pending. The IRP will be completed when a buffer is
available.
Arguments:
Irp -
The IRP containing the allocation method request.
Method -
The method parameter of the allocation request.
Data -
The data paramter of the allocation request.
Return Value:
Returns STATUS_SUCCESS if the buffer was allocated, else STATUS_PENDING.
--*/
{
NTSTATUS Status;
PAGED_CODE();
//
// Do not let user mode have direct access to this.
//
if (Irp->RequestorMode != KernelMode) {
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// First just try to allocate a frame. If this succeeds, then a
// successful return can be generated. If not, the Irp is just
// queued if we are out of entries, otherwise we complete the IRP
// with a failure status.
//
Status = iMethodAlloc( Irp, Method, Data );
if (Status == STATUS_NO_MORE_ENTRIES) {
PIO_STACK_LOCATION irpSp;
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
irpSp = IoGetCurrentIrpStackLocation( Irp );
DefAllocatorInstance =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) irpSp->FileObject->FsContext;
//
// The new frame waiter is added to the end of the queue so that
// all requests are serviced approximately in order.
//
KsAddIrpToCancelableQueue( &DefAllocatorInstance->WaiterQueue,
&DefAllocatorInstance->WaiterLock,
Irp, KsListEntryTail, KsCancelRoutine );
Status = STATUS_PENDING;
}
return Status;
}
VOID
ServiceAllocRequests(
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance
)
/*++
Routine Description:
Attempts to fulfill all outstanding allocation requests.
Arguments:
DefAllocatorInstance -
Contains the allocator instance information.
Return Value:
Nothing.
--*/
{
PIRP AllocIrp;
//
// Note that this may be competing for the free item with the DISPATCH_LEVEL
// interface. If an Irp can be pulled off of the queue, then it can be
// completed, otherwise it's already been grabbed by another waiter.
//
while (AllocIrp =
KsRemoveIrpFromCancelableQueue( &DefAllocatorInstance->WaiterQueue,
&DefAllocatorInstance->WaiterLock,
KsListEntryHead,
KsAcquireOnly )) {
//
// Try to complete the I/O by processing the request again. If it
// fails to allocate the frame, just put the waiter back on the list.
//
if (STATUS_SUCCESS ==
KsDispatchSpecificMethod( AllocIrp, iMethodAlloc )) {
KsRemoveSpecificIrpFromCancelableQueue( AllocIrp );
AllocIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( AllocIrp, IO_NO_INCREMENT );
} else {
KsReleaseIrpOnCancelableQueue( AllocIrp, NULL );
break;
}
}
}
VOID
FreeWorker(
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance
)
/*++
Routine Description:
The worker callback function which is used to attempt to fulfill any
outstanding allocation requests.
Arguments:
DefAllocatorInstance -
Contains the allocator instance information.
Return Value:
Nothing.
--*/
{
//
// Ensure that the reference count is updated first, so that the
// DefAllocatorClose check won't skip a wait.
//
InterlockedIncrement(&DefAllocatorInstance->ReferenceCount);
DefAllocatorInstance->FreeWorkItem.List.Blink = NULL;
ServiceAllocRequests(DefAllocatorInstance);
//
// Only set the event if another work item is not about to increment
// the reference count on the way into this function.
//
if (!InterlockedDecrement(&DefAllocatorInstance->ReferenceCount) &&
!DefAllocatorInstance->FreeWorkItem.List.Blink &&
DefAllocatorInstance->ClosingAllocator) {
KeSetEvent(&DefAllocatorInstance->Event, IO_NO_INCREMENT, FALSE);
}
}
NTSTATUS
MethodFree(
IN PIRP Irp,
IN PKSMETHOD Method,
IN PVOID Data
)
/*++
Routine Description:
This function is the method handler for KSMETHOD_STREAMALLOCATOR_FREE.
Attempts to free memory using the internal direct function call free
routine.
Arguments:
Irp -
The specific free method IRP to be processed.
Method -
Points to the method identifier parameter.
Data -
Points to the memory block pointer to free.
Return Value:
Returns STATUS_SUCCESS.
--*/
{
PIO_STACK_LOCATION irpSp;
PAGED_CODE();
//
// Do not let user mode have direct access to this.
//
if (Irp->RequestorMode != KernelMode) {
return STATUS_INVALID_DEVICE_REQUEST;
}
irpSp = IoGetCurrentIrpStackLocation( Irp );
iFree( irpSp->FileObject, *((PVOID *)Data ) );
//
// Since a free frame has just been added, try servicing any free requests.
//
ServiceAllocRequests( (PKSDEFAULTALLOCATOR_INSTANCEHDR) irpSp->FileObject->FsContext );
return STATUS_SUCCESS;
}
NTSTATUS
DefAllocatorGetFunctionTable(
IN PIRP Irp,
IN PKSPROPERTY Property,
OUT PKSSTREAMALLOCATOR_FUNCTIONTABLE FunctionTable
)
/*++
Routine Description:
This function is the method handler for
KSMETHOD_STREAMALLOCATOR_FUNCTIONTABLE. Returns the allocate and free
functions.
Arguments:
Irp -
The specific property IRP to be processed.
Property -
Points to the property identifier parameter.
FunctionTable -
Points to the function table to fill in.
Return Value:
--*/
{
PAGED_CODE();
//
// Fill caller's function table with the DISPATCH_LEVEL interfaces
// for allocate and free.
//
FunctionTable->AllocateFrame = iAlloc;
FunctionTable->FreeFrame = iFreeAndStartWorker;
//
// This field has already been set by the property handling.
// Irp->IoStatus.Information = sizeof( *FunctionTable );
//
return STATUS_SUCCESS;
}
NTSTATUS
DefAllocatorGetStatus(
IN PIRP Irp,
IN PKSPROPERTY Property,
OUT PKSSTREAMALLOCATOR_STATUS Status
)
/*++
Routine Description:
This function is the method handler for KSPROPERTY_STREAMALLOCATOR_STATUS
which returns the current status of the given allocator.
Arguments:
Irp -
The specific property IRP to be processed.
Property -
Points to the property identifier parameter.
Status -
Points to the status structure.
Return Value:
--*/
{
PIO_STACK_LOCATION irpSp;
PKSDEFAULTALLOCATOR_INSTANCEHDR DefAllocatorInstance;
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation( Irp );
DefAllocatorInstance =
(PKSDEFAULTALLOCATOR_INSTANCEHDR) irpSp->FileObject->FsContext;
//
// Return the allocator status information.
//
Status->AllocatedFrames = DefAllocatorInstance->AllocatedFrames;
Status->Framing = DefAllocatorInstance->Framing;
Status->Reserved = 0;
//
// This field has already been set by the property handling.
// Irp->IoStatus.Information = sizeof( *Status );
//
return STATUS_SUCCESS;
}