1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2025-11-11 02:58:02 -06:00

Windows Driver: Make max work items count configurable. Increase default to 1024. Queue write IRPs.

- Made the maximum work items count configurable to allow flexibility based on system needs.
  - Increased the default value of max work items count to 1024 to better handle high-throughput scenarios.
  - Queue write IRPs in system worker thread to avoid potential deadlocks in write scenarios.
This commit is contained in:
Mounir IDRASSI
2024-11-23 17:44:48 +01:00
parent 5a85c54c6e
commit 453ff2880e
5 changed files with 56 additions and 38 deletions

View File

@@ -396,6 +396,7 @@ typedef struct
int EncryptionIoRequestCount; int EncryptionIoRequestCount;
int EncryptionItemCount; int EncryptionItemCount;
int EncryptionFragmentSize; int EncryptionFragmentSize;
int EncryptionMaxWorkItems;
} EncryptionQueueParameters; } EncryptionQueueParameters;
#pragma pack (pop) #pragma pack (pop)
@@ -418,6 +419,7 @@ typedef struct
#define VC_ENCRYPTION_IO_REQUEST_COUNT DRIVER_STR("VeraCryptEncryptionIoRequestCount") #define VC_ENCRYPTION_IO_REQUEST_COUNT DRIVER_STR("VeraCryptEncryptionIoRequestCount")
#define VC_ENCRYPTION_ITEM_COUNT DRIVER_STR("VeraCryptEncryptionItemCount") #define VC_ENCRYPTION_ITEM_COUNT DRIVER_STR("VeraCryptEncryptionItemCount")
#define VC_ENCRYPTION_FRAGMENT_SIZE DRIVER_STR("VeraCryptEncryptionFragmentSize") #define VC_ENCRYPTION_FRAGMENT_SIZE DRIVER_STR("VeraCryptEncryptionFragmentSize")
#define VC_ENCRYPTION_MAX_WORK_ITEMS DRIVER_STR("VeraCryptEncryptionMaxWorkItems")
#define VC_ERASE_KEYS_SHUTDOWN DRIVER_STR("VeraCryptEraseKeysShutdown") #define VC_ERASE_KEYS_SHUTDOWN DRIVER_STR("VeraCryptEraseKeysShutdown")

View File

@@ -303,8 +303,43 @@ static VOID CompleteIrpWorkItemRoutine(PDEVICE_OBJECT DeviceObject, PVOID Contex
} }
} }
// Handles the completion of the original IRP.
static VOID HandleCompleteOriginalIrp(EncryptedIoQueue* queue, EncryptedIoRequest* request)
{
NTSTATUS status = KeWaitForSingleObject(&queue->WorkItemSemaphore, Executive, KernelMode, FALSE, NULL);
if (queue->ThreadExitRequested)
return;
if (!NT_SUCCESS(status))
{
// Handle wait failure: we call the completion routine directly.
// This is not ideal since it can cause deadlock that we are trying to fix but it is better than losing the IRP.
CompleteOriginalIrp(request->Item, STATUS_INSUFFICIENT_RESOURCES, 0);
}
else
{
// Obtain a work item from the free list.
KIRQL oldIrql;
KeAcquireSpinLock(&queue->WorkItemLock, &oldIrql);
PLIST_ENTRY freeEntry = RemoveHeadList(&queue->FreeWorkItemsList);
KeReleaseSpinLock(&queue->WorkItemLock, oldIrql);
PCOMPLETE_IRP_WORK_ITEM workItem = CONTAINING_RECORD(freeEntry, COMPLETE_IRP_WORK_ITEM, ListEntry);
// Increment ActiveWorkItems.
InterlockedIncrement(&queue->ActiveWorkItems);
KeResetEvent(&queue->NoActiveWorkItemsEvent);
// Prepare the work item.
workItem->Irp = request->Item->OriginalIrp;
workItem->Status = request->Item->Status;
workItem->Information = NT_SUCCESS(request->Item->Status) ? request->Item->OriginalLength : 0;
workItem->Item = request->Item;
// Queue the work item.
IoQueueWorkItem(workItem->WorkItem, CompleteIrpWorkItemRoutine, DelayedWorkQueue, workItem);
}
}
static VOID CompletionThreadProc(PVOID threadArg) static VOID CompletionThreadProc(PVOID threadArg)
{ {
@@ -348,39 +383,7 @@ static VOID CompletionThreadProc(PVOID threadArg)
if (request->CompleteOriginalIrp) if (request->CompleteOriginalIrp)
{ {
// Wait for a work item to become available HandleCompleteOriginalIrp(queue, request);
NTSTATUS status = KeWaitForSingleObject(&queue->WorkItemSemaphore, Executive, KernelMode, FALSE, NULL);
if (queue->ThreadExitRequested)
break;
if (!NT_SUCCESS(status))
{
// Handle wait failure: we call the completion routine directly.
// This is not ideal since it can cause deadlock that we are trying to fix but it is better than losing the IRP.
CompleteOriginalIrp(request->Item, STATUS_INSUFFICIENT_RESOURCES, 0);
}
else
{
// Obtain a work item from the free list
KIRQL oldIrql;
KeAcquireSpinLock(&queue->WorkItemLock, &oldIrql);
PLIST_ENTRY freeEntry = RemoveHeadList(&queue->FreeWorkItemsList);
KeReleaseSpinLock(&queue->WorkItemLock, oldIrql);
PCOMPLETE_IRP_WORK_ITEM workItem = CONTAINING_RECORD(freeEntry, COMPLETE_IRP_WORK_ITEM, ListEntry);
// Increment ActiveWorkItems
InterlockedIncrement(&queue->ActiveWorkItems);
KeResetEvent(&queue->NoActiveWorkItemsEvent);
// Prepare the work item
workItem->Irp = request->Item->OriginalIrp;
workItem->Status = request->Item->Status;
workItem->Information = NT_SUCCESS(request->Item->Status) ? request->Item->OriginalLength : 0;
workItem->Item = request->Item;
// Queue the work item
IoQueueWorkItem(workItem->WorkItem, CompleteIrpWorkItemRoutine, DelayedWorkQueue, workItem);
}
} }
ReleasePoolBuffer(queue, request); ReleasePoolBuffer(queue, request);
@@ -541,8 +544,7 @@ static VOID IoThreadProc (PVOID threadArg)
if (request->CompleteOriginalIrp) if (request->CompleteOriginalIrp)
{ {
CompleteOriginalIrp (request->Item, request->Item->Status, HandleCompleteOriginalIrp(queue, request);
NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0);
} }
ReleasePoolBuffer (queue, request); ReleasePoolBuffer (queue, request);
@@ -1148,10 +1150,10 @@ retry_preallocated:
// Initialize the free work item list // Initialize the free work item list
InitializeListHead(&queue->FreeWorkItemsList); InitializeListHead(&queue->FreeWorkItemsList);
KeInitializeSemaphore(&queue->WorkItemSemaphore, VC_MAX_WORK_ITEMS, VC_MAX_WORK_ITEMS); KeInitializeSemaphore(&queue->WorkItemSemaphore, EncryptionMaxWorkItems, EncryptionMaxWorkItems);
KeInitializeSpinLock(&queue->WorkItemLock); KeInitializeSpinLock(&queue->WorkItemLock);
queue->MaxWorkItems = VC_MAX_WORK_ITEMS; queue->MaxWorkItems = EncryptionMaxWorkItems;
queue->WorkItemPool = (PCOMPLETE_IRP_WORK_ITEM)TCalloc(sizeof(COMPLETE_IRP_WORK_ITEM) * queue->MaxWorkItems); queue->WorkItemPool = (PCOMPLETE_IRP_WORK_ITEM)TCalloc(sizeof(COMPLETE_IRP_WORK_ITEM) * queue->MaxWorkItems);
if (!queue->WorkItemPool) if (!queue->WorkItemPool)
{ {

View File

@@ -26,7 +26,7 @@
#define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT 16 #define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT 16
#define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_MAX_COUNT 8192 #define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_MAX_COUNT 8192
#define VC_MAX_WORK_ITEMS 256 #define VC_MAX_WORK_ITEMS 1024
typedef struct EncryptedIoQueueBufferStruct typedef struct EncryptedIoQueueBufferStruct
{ {

View File

@@ -145,6 +145,7 @@ static BOOL RamEncryptionActivated = FALSE;
int EncryptionIoRequestCount = 0; int EncryptionIoRequestCount = 0;
int EncryptionItemCount = 0; int EncryptionItemCount = 0;
int EncryptionFragmentSize = 0; int EncryptionFragmentSize = 0;
int EncryptionMaxWorkItems = 0;
PDEVICE_OBJECT VirtualVolumeDeviceObjects[MAX_MOUNTED_VOLUME_DRIVE_NUMBER + 1]; PDEVICE_OBJECT VirtualVolumeDeviceObjects[MAX_MOUNTED_VOLUME_DRIVE_NUMBER + 1];
@@ -2776,6 +2777,7 @@ NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex
if (ValidateIOBufferSize (Irp, sizeof (EncryptionQueueParameters), ValidateOutput)) if (ValidateIOBufferSize (Irp, sizeof (EncryptionQueueParameters), ValidateOutput))
{ {
EncryptionQueueParameters* pParams = (EncryptionQueueParameters*) Irp->AssociatedIrp.SystemBuffer; EncryptionQueueParameters* pParams = (EncryptionQueueParameters*) Irp->AssociatedIrp.SystemBuffer;
pParams->EncryptionMaxWorkItems = EncryptionMaxWorkItems;
pParams->EncryptionFragmentSize = EncryptionFragmentSize; pParams->EncryptionFragmentSize = EncryptionFragmentSize;
pParams->EncryptionIoRequestCount = EncryptionIoRequestCount; pParams->EncryptionIoRequestCount = EncryptionIoRequestCount;
pParams->EncryptionItemCount = EncryptionItemCount; pParams->EncryptionItemCount = EncryptionItemCount;
@@ -4646,6 +4648,14 @@ NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry)
TCfree (data); TCfree (data);
} }
if (driverEntry && NT_SUCCESS(TCReadRegistryKey(&name, VC_ENCRYPTION_MAX_WORK_ITEMS, &data)))
{
if (data->Type == REG_DWORD)
EncryptionMaxWorkItems = *(uint32*)data->Data;
TCfree(data);
}
if (driverEntry) if (driverEntry)
{ {
if (EncryptionIoRequestCount < TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT) if (EncryptionIoRequestCount < TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT)
@@ -4664,6 +4674,9 @@ NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry)
else if (EncryptionFragmentSize > (8 * TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE)) else if (EncryptionFragmentSize > (8 * TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE))
EncryptionFragmentSize = 8 * TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; EncryptionFragmentSize = 8 * TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE;
if (EncryptionMaxWorkItems == 0)
EncryptionMaxWorkItems = VC_MAX_WORK_ITEMS;
} }

View File

@@ -128,6 +128,7 @@ extern BOOL AllowWindowsDefrag;
extern int EncryptionIoRequestCount; extern int EncryptionIoRequestCount;
extern int EncryptionItemCount; extern int EncryptionItemCount;
extern int EncryptionFragmentSize; extern int EncryptionFragmentSize;
extern int EncryptionMaxWorkItems;
extern BOOL EraseKeysOnShutdown; extern BOOL EraseKeysOnShutdown;
/* Helper macro returning x seconds in units of 100 nanoseconds */ /* Helper macro returning x seconds in units of 100 nanoseconds */
#define WAIT_SECONDS(x) ((x)*10000000) #define WAIT_SECONDS(x) ((x)*10000000)