Windows NT Driver >> C/C++
Cancel-Safe IRP Queue(CSQ)
參考資訊:
1. Source Code
2. cancel-safe-irp-queues
Cancel-Safe IRP Queues(CSQ)是Microsoft提供的一個Cancel框架,這個框架概念就是讓使用者只專注在Cancel的資料處理上,而不是Cancel的同步處理上,畢竟每個人做出來的Cancel同步機制可能不同,但是原理是一樣的,因此,Microsoft提供了一個專門框架給驅動程式使用並且建議使用這個框架處理Cancel Irp,下圖是說明的圖片:
對於CSQ細節有興趣的使用者可以參考上面的參考資訊,而設定的Callback如下所示:
1. CsqInsertIrp
2. CsqRemoveIrp
3. CsqPeekNextIrp
4. CsqAcquireLock
5. CsqReleaseLock
6. CsqCompleteCanceledIrp
main.c
#include <wdm.h> #define IOCTL_QUEUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) #define DEV_NAME L"\\Device\\MyDriver" #define SYM_NAME L"\\DosDevices\\MyDriver" KDPC stDPC={0}; IO_CSQ stCsq={0}; KTIMER stTime={0}; KSPIN_LOCK stLock={0}; LIST_ENTRY stQueue={0}; VOID CsqInsertIrp(struct _IO_CSQ *pCsq, PIRP pIrp) { DbgPrint("CsqInsertIrp"); InsertTailList(&stQueue, &pIrp->Tail.Overlay.ListEntry); } VOID CsqRemoveIrp(PIO_CSQ pCsq, PIRP pIrp) { UNREFERENCED_PARAMETER(pCsq); DbgPrint("CsqRemoveIrp"); RemoveEntryList(&pIrp->Tail.Overlay.ListEntry); } VOID CsqCompleteCanceledIrp(PIO_CSQ pCsq, PIRP pIrp) { UNREFERENCED_PARAMETER(pCsq); DbgPrint("CsqCompleteCanceledIrp"); pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } PIRP CsqPeekNextIrp(PIO_CSQ pCsq, PIRP pIrp, PVOID PeekContext) { PIRP pNextIrp=NULL; PLIST_ENTRY pList=NULL; PLIST_ENTRY pNext=NULL; PIO_STACK_LOCATION psk=NULL; pList = &stQueue; if(pIrp == NULL){ pNext = pList->Flink; } else{ pNext = pIrp->Tail.Overlay.ListEntry.Flink; } while(pNext != pList){ pNextIrp = CONTAINING_RECORD(pNext, IRP, Tail.Overlay.ListEntry); psk = IoGetCurrentIrpStackLocation(pNextIrp); if(PeekContext){ if(psk->FileObject == (PFILE_OBJECT)PeekContext){ break; } } else{ break; } pNextIrp = NULL; pNext = pNext->Flink; } return pNextIrp; } VOID CsqAcquireLock(PIO_CSQ pCsq, KIRQL *pIrql) { DbgPrint("CsqAcquireLock"); KeAcquireSpinLock(&stLock, pIrql); } VOID CsqReleaseLock(PIO_CSQ pCsq, KIRQL pIrql) { if(pIrql == DISPATCH_LEVEL){ KeReleaseSpinLockFromDpcLevel(&stLock); DbgPrint("CsqReleaseLock at DPC level"); } else{ KeReleaseSpinLock(&stLock, pIrql); DbgPrint("CsqReleaseLock at Passive level"); } } VOID OnTimer(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { PIRP pIrp; PLIST_ENTRY plist; if(IsListEmpty(&stQueue) == TRUE){ KeCancelTimer(&stTime); DbgPrint("Finish"); } else{ plist = RemoveHeadList(&stQueue); pIrp = CONTAINING_RECORD(plist, IRP, Tail.Overlay.ListEntry); if(pIrp->Cancel != TRUE){ pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); DbgPrint("Complete Irp"); } else{ pIrp->CancelRoutine = NULL; pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); DbgPrint("Cancel Irp"); } } } void Unload(PDRIVER_OBJECT pOurDriver) { UNICODE_STRING usSymboName; RtlInitUnicodeString(&usSymboName, L"\\DosDevices\\MyDriver"); IoDeleteSymbolicLink(&usSymboName); IoDeleteDevice(pOurDriver->DeviceObject); } NTSTATUS IrpIOCTL(PDEVICE_OBJECT pOurDevice, PIRP pIrp) { LARGE_INTEGER stTimePeriod; PIO_STACK_LOCATION psk = IoGetCurrentIrpStackLocation(pIrp); switch(psk->Parameters.DeviceIoControl.IoControlCode){ case IOCTL_QUEUE: DbgPrint("IOCTL_QUEUE"); IoCsqInsertIrp(&stCsq, pIrp, NULL); return STATUS_PENDING; case IOCTL_PROCESS: DbgPrint("IOCTL_PROCESS"); stTimePeriod.HighPart|= -1; stTimePeriod.LowPart = -1000000; KeSetTimerEx(&stTime, stTimePeriod, 1000, &stDPC); break; } pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS IrpFile(PDEVICE_OBJECT pOurDevice, PIRP pIrp) { PIO_STACK_LOCATION psk = IoGetCurrentIrpStackLocation(pIrp); switch(psk->MajorFunction){ case IRP_MJ_CREATE: DbgPrint("IRP_MJ_CREATE"); break; case IRP_MJ_CLOSE: DbgPrint("IRP_MJ_CLOSE"); break; } IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry(PDRIVER_OBJECT pOurDriver, PUNICODE_STRING pOurRegistry) { PDEVICE_OBJECT pOurDevice=NULL; UNICODE_STRING usDeviceName; UNICODE_STRING usSymboName; pOurDriver->MajorFunction[IRP_MJ_CREATE] = pOurDriver->MajorFunction[IRP_MJ_CLOSE] = IrpFile; pOurDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpIOCTL; pOurDriver->DriverUnload = Unload; RtlInitUnicodeString(&usDeviceName, L"\\Device\\MyDriver"); IoCreateDevice(pOurDriver, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pOurDevice); RtlInitUnicodeString(&usSymboName, L"\\DosDevices\\MyDriver"); InitializeListHead(&stQueue); KeInitializeSpinLock(&stLock); KeInitializeTimer(&stTime); KeInitializeDpc(&stDPC, OnTimer, pOurDevice); IoCsqInitialize(&stCsq, CsqInsertIrp, CsqRemoveIrp, CsqPeekNextIrp, CsqAcquireLock, CsqReleaseLock, CsqCompleteCanceledIrp); IoCreateSymbolicLink(&usSymboName, &usDeviceName); pOurDevice->Flags&= ~DO_DEVICE_INITIALIZING; pOurDevice->Flags|= DO_BUFFERED_IO; return STATUS_SUCCESS; }
DriverEntry()初始化List、Spinlock、DPC Timer和CSQ Callback
IrpIOCTL()IOCTL_QUEUE把Irp加入List,IOCTL_PROCESS開始處理Irp
OnTimer()定時查看是否有需要處理的Irp
CsqReleaseLock()同步CSQ Irp用
CsqAcquireLock()同步CSQ Irp用
CsqPeekNextIrp()需要手動移除CSQ Irp時使用,目前沒有使用到
CsqCompleteCanceledIrp處理Cancel Irp用
CsqRemoveIrp()Irp被CSQ移除時呼叫
CsqInsertIrp()Irp被加入到CSQ時呼叫
app.c
#define INITGUID #include <windows.h> #include <winioctl.h> #include <strsafe.h> #include <setupapi.h> #include <stdio.h> #include <stdlib.h> #define IOCTL_QUEUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) int __cdecl main(int argc, char* argv[]) { int i=0; DWORD dwRet = 0; HANDLE hFile = NULL; OVERLAPPED ov[3]={0}; hFile = CreateFile("\\\\.\\MyDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("failed to open mydriver\n"); return -1; } for(i=0; i<3; i++){ memset(&ov[i], 0, sizeof(ov[i])); ov[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); printf("queue event\n"); DeviceIoControl(hFile, IOCTL_QUEUE, NULL, 0, NULL, 0, &dwRet, &ov[i]); } printf("process all of events\n"); DeviceIoControl(hFile, IOCTL_PROCESS, NULL, 0, NULL, 0, &dwRet, NULL); Sleep(1000); CancelIo(hFile); for(i=0; i<3; i++){ WaitForSingleObject(ov[i].hEvent, INFINITE); CloseHandle(ov[i].hEvent); printf("wait complete\n"); } CloseHandle(hFile); return 0; }
結果