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;
}

結果


返回上一頁