/***************************************************************************** * callback.cpp - Generic unload safe callbacks (where possible) ***************************************************************************** * Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved. */ #include "private.h" VOID EnqueuedIoWorkItemCallback( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ); VOID EnqueuedDpcCallback( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); #pragma code_seg() NTSTATUS CallbackEnqueue( IN OUT PVOID *pCallbackHandle OPTIONAL, IN PFNQUEUED_CALLBACK CallbackRoutine, IN PDEVICE_OBJECT DeviceObject, IN PVOID Context, IN KIRQL Irql, IN ULONG Flags ) { PQUEUED_CALLBACK_ITEM pQueuedCallbackItem; // // Check the flags we understand. If it's not understood, and this is the // class of flags support is required for, bail immediately. // if ((Flags & (~EQCM_SUPPORTED_FLAGS)) & EQCM_SUPPORT_OR_FAIL_FLAGS) { return STATUS_NOT_SUPPORTED; } if ((Irql != PASSIVE_LEVEL) && (Irql != DISPATCH_LEVEL)) { ASSERT(0); return STATUS_NOT_SUPPORTED; } if (Flags & EQCF_REUSE_HANDLE) { ASSERT(pCallbackHandle); pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) *pCallbackHandle; // // Shouldn't already be enqueued. // ASSERT(pQueuedCallbackItem->Enqueued == 0); } else { pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) ExAllocatePoolWithTag( ((KeGetCurrentIrql() == PASSIVE_LEVEL) && (Irql == PASSIVE_LEVEL)) ? PagedPool : NonPagedPool, sizeof(QUEUED_CALLBACK_ITEM), 'bCcP' ); // 'PcCb' if (pQueuedCallbackItem) { pQueuedCallbackItem->ReentrancyCount = 0; pQueuedCallbackItem->IoWorkItem = IoAllocateWorkItem( DeviceObject ); if (pQueuedCallbackItem->IoWorkItem == NULL) { ExFreePool(pQueuedCallbackItem); pQueuedCallbackItem = NULL; } } } if (ARGUMENT_PRESENT(pCallbackHandle)) { *pCallbackHandle = pQueuedCallbackItem; } if (pQueuedCallbackItem) { pQueuedCallbackItem->QueuedCallback = CallbackRoutine; pQueuedCallbackItem->DeviceObject = DeviceObject; pQueuedCallbackItem->Context = Context; pQueuedCallbackItem->Flags = Flags; pQueuedCallbackItem->Irql = Irql; pQueuedCallbackItem->Enqueued = 1; if ((!(Flags&EQCF_DIFFERENT_THREAD_REQUIRED)) && (KeGetCurrentIrql() == Irql)&& (pQueuedCallbackItem->ReentrancyCount < MAX_THREAD_REENTRANCY)) { pQueuedCallbackItem->ReentrancyCount++; EnqueuedIoWorkItemCallback(DeviceObject, (PVOID) pQueuedCallbackItem); } else { pQueuedCallbackItem->ReentrancyCount = 0; if (Irql == PASSIVE_LEVEL) { IoQueueWorkItem( pQueuedCallbackItem->IoWorkItem, EnqueuedIoWorkItemCallback, DelayedWorkQueue, pQueuedCallbackItem ); } else { ASSERT(Irql == DISPATCH_LEVEL); KeInitializeDpc( &pQueuedCallbackItem->Dpc, EnqueuedDpcCallback, pQueuedCallbackItem ); KeInsertQueueDpc( &pQueuedCallbackItem->Dpc, NULL, NULL ); } } return STATUS_SUCCESS; } else { return STATUS_INSUFFICIENT_RESOURCES; } } NTSTATUS CallbackCancel( IN PVOID pCallbackHandle ) { PQUEUED_CALLBACK_ITEM pQueuedCallbackItem; pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) pCallbackHandle; if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) { // // We got it. If it's DPC, also try to yank it from the queue. // if (pQueuedCallbackItem->Irql == DISPATCH_LEVEL) { KeRemoveQueueDpc(&pQueuedCallbackItem->Dpc); } return STATUS_SUCCESS; } else { // // Caller beat us to it... // return STATUS_UNSUCCESSFUL; } } VOID CallbackFree( IN PVOID pCallbackHandle ) { PQUEUED_CALLBACK_ITEM pQueuedCallbackItem; pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) pCallbackHandle; ASSERT(pQueuedCallbackItem->Enqueued == 0); IoFreeWorkItem(pQueuedCallbackItem->IoWorkItem); ExFreePool(pQueuedCallbackItem); } VOID EnqueuedDpcCallback( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PQUEUED_CALLBACK_ITEM pQueuedCallbackItem; QUEUED_CALLBACK_RETURN returnValue; NTSTATUS ntStatus; pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) DeferredContext; if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) { returnValue = pQueuedCallbackItem->QueuedCallback( pQueuedCallbackItem->DeviceObject, pQueuedCallbackItem->Context ); } else { returnValue = QUEUED_CALLBACK_RETAIN; } switch(returnValue) { case QUEUED_CALLBACK_FREE: CallbackFree((PVOID) pQueuedCallbackItem); break; case QUEUED_CALLBACK_RETAIN: // // Nothing to do in this case, in fact we don't dare touch anything // in the structure lest it be already freed. // break; case QUEUED_CALLBACK_REISSUE: // // Re-enqueue it with the same handle to avoid reallocation. // ntStatus = CallbackEnqueue( (PVOID *) &pQueuedCallbackItem, pQueuedCallbackItem->QueuedCallback, pQueuedCallbackItem->DeviceObject, pQueuedCallbackItem->Context, pQueuedCallbackItem->Irql, pQueuedCallbackItem->Flags | EQCF_REUSE_HANDLE ); ASSERT(NT_SUCCESS(ntStatus)); break; default: ASSERT(0); break; } } #pragma code_seg("PAGE") VOID EnqueuedIoWorkItemCallback( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PQUEUED_CALLBACK_ITEM pQueuedCallbackItem; QUEUED_CALLBACK_RETURN returnValue; NTSTATUS ntStatus; PAGED_CODE(); pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) Context; ASSERT(pQueuedCallbackItem->DeviceObject == DeviceObject); if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) { returnValue = pQueuedCallbackItem->QueuedCallback( pQueuedCallbackItem->DeviceObject, pQueuedCallbackItem->Context ); } else { returnValue = QUEUED_CALLBACK_RETAIN; } switch(returnValue) { case QUEUED_CALLBACK_FREE: CallbackFree((PVOID) pQueuedCallbackItem); break; case QUEUED_CALLBACK_RETAIN: // // Nothing to do in this case, in fact we don't dare touch anything // in the structure lest it be already freed. // break; case QUEUED_CALLBACK_REISSUE: // // Re-enqueue it with the same handle to avoid reallocation. // ntStatus = CallbackEnqueue( (PVOID *) &pQueuedCallbackItem, pQueuedCallbackItem->QueuedCallback, pQueuedCallbackItem->DeviceObject, pQueuedCallbackItem->Context, pQueuedCallbackItem->Irql, pQueuedCallbackItem->Flags | EQCF_REUSE_HANDLE ); ASSERT(NT_SUCCESS(ntStatus)); break; default: ASSERT(0); break; } }