[NTOSKRNL] Implement the support for reserve IRP in IO
authorPierre Schweitzer <pierre@reactos.org>
Sat, 24 Feb 2018 13:50:23 +0000 (14:50 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Sat, 24 Feb 2018 13:52:04 +0000 (14:52 +0100)
The reserve IRP is an IRP which is allocated on system boot and kept during
the whole system life. Its purpose is to allow page reads in case of
low-memory situations where the system doesn't have enough memory left
to allocate an IRP to read from the page file (would be catastrophic situation).

ntoskrnl/include/internal/io.h
ntoskrnl/io/iomgr/iofunc.c
ntoskrnl/io/iomgr/iomgr.c
ntoskrnl/io/iomgr/irp.c

index 2a4c62f..86ddbda 100644 (file)
@@ -513,6 +513,18 @@ typedef struct _DEVICETREE_TRAVERSE_CONTEXT
     PVOID Context;
 } DEVICETREE_TRAVERSE_CONTEXT, *PDEVICETREE_TRAVERSE_CONTEXT;
 
+//
+// Reserve IRP allocator
+// Used for read paging IOs in low-memory situations
+//
+typedef struct _RESERVE_IRP_ALLOCATOR
+{
+    PIRP ReserveIrp;
+    volatile LONG ReserveIrpInUse;
+    KEVENT WaitEvent;
+    CCHAR StackSize;
+} RESERVE_IRP_ALLOCATOR, *PRESERVE_IRP_ALLOCATOR;
+
 //
 // Resource code
 //
@@ -920,6 +932,18 @@ IopAllocateIrpMustSucceed(
     IN CCHAR StackSize
 );
 
+BOOLEAN
+NTAPI
+IopInitializeReserveIrp(
+    IN PRESERVE_IRP_ALLOCATOR ReserveIrpAllocator
+);
+
+PIRP
+NTAPI
+IopAllocateReserveIrp(
+    IN CCHAR StackSize
+);
+
 //
 // Shutdown routines
 //
@@ -1317,6 +1341,7 @@ extern PIO_BUS_TYPE_GUID_LIST PnpBusTypeGuidList;
 extern PDRIVER_OBJECT IopRootDriverObject;
 extern KSPIN_LOCK IopDeviceRelationsSpinLock;
 extern LIST_ENTRY IopDeviceRelationsRequestList;
+extern RESERVE_IRP_ALLOCATOR IopReserveIrpAllocator;
 
 //
 // Inlined Functions
index aa08bf7..fa48869 100644 (file)
@@ -17,6 +17,9 @@
 #include <debug.h>
 #include "internal/io_i.h"
 
+volatile LONG IoPageReadIrpAllocationFailure = 0;
+volatile LONG IoPageReadNonPagefileIrpAllocationFailure = 0;
+
 /* PRIVATE FUNCTIONS *********************************************************/
 
 VOID
@@ -1099,7 +1102,30 @@ IoPageRead(IN PFILE_OBJECT FileObject,
 
     /* Allocate IRP */
     Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
-    if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
+    /* If allocation failed, try to see whether we can use
+     * the reserve IRP
+     */
+    if (Irp == NULL)
+    {
+        /* We will use it only for paging file */
+        if (MmIsFileObjectAPagingFile(FileObject))
+        {
+            InterlockedExchangeAdd(&IoPageReadIrpAllocationFailure, 1);
+            Irp = IopAllocateReserveIrp(DeviceObject->StackSize);
+        }
+        else
+        {
+            InterlockedExchangeAdd(&IoPageReadNonPagefileIrpAllocationFailure, 1);
+        }
+
+        /* If allocation failed (not a paging file or too big stack size)
+         * Fail for real
+         */
+        if (Irp == NULL)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
 
     /* Get the Stack */
     StackPtr = IoGetNextIrpStackLocation(Irp);
index 0676e97..47defca 100644 (file)
@@ -502,6 +502,13 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     KeInitializeSpinLock(&ShutdownListLock);
     KeInitializeSpinLock(&IopLogListLock);
 
+    /* Initialize the reserve IRP */
+    if (!IopInitializeReserveIrp(&IopReserveIrpAllocator))
+    {
+        DPRINT1("IopInitializeReserveIrp failed!\n");
+        return FALSE;
+    }
+
     /* Initialize Timer List Lock */
     KeInitializeSpinLock(&IopTimerLock);
 
index 856aae2..b1de62d 100644 (file)
@@ -6,6 +6,7 @@
  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  *                  Gunnar Dalsnes
  *                  Filip Navara (navaraf@reactos.org)
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES ****************************************************************/
@@ -15,6 +16,7 @@
 #include <debug.h>
 
 PIRP IopDeadIrp;
+RESERVE_IRP_ALLOCATOR IopReserveIrpAllocator;
 
 /* PRIVATE FUNCTIONS  ********************************************************/
 
@@ -542,6 +544,67 @@ IopCompleteRequest(IN PKAPC Apc,
     }
 }
 
+BOOLEAN
+NTAPI
+IopInitializeReserveIrp(IN PRESERVE_IRP_ALLOCATOR ReserveIrpAllocator)
+{
+    /* Our allocated stack size */
+    ReserveIrpAllocator->StackSize = 20;
+
+    /* Allocate the IRP now */
+    ReserveIrpAllocator->ReserveIrp = IoAllocateIrp(ReserveIrpAllocator->StackSize, FALSE);
+    /* If we cannot, abort system boot */
+    if (ReserveIrpAllocator->ReserveIrp == NULL)
+    {
+        return FALSE;
+    }
+
+    /* It's not in use */
+    ReserveIrpAllocator->ReserveIrpInUse = 0;
+    /* And init the event */
+    KeInitializeEvent(&ReserveIrpAllocator->WaitEvent, SynchronizationEvent, FALSE);
+
+    /* All good, keep booting */
+    return TRUE;
+}
+
+PIRP
+NTAPI
+IopAllocateReserveIrp(IN CCHAR StackSize)
+{
+    /* If we need a stack size higher than what was allocated, then fail */
+    if (StackSize > IopReserveIrpAllocator.StackSize)
+    {
+        return NULL;
+    }
+
+    /* Now, wait until the IRP becomes available and reserve it immediately */
+    while (InterlockedExchange(&IopReserveIrpAllocator.ReserveIrpInUse, 1) == 1)
+    {
+        KeWaitForSingleObject(&IopReserveIrpAllocator.WaitEvent,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+    }
+
+    /* It's ours! Initialize it */
+    IoInitializeIrp(IopReserveIrpAllocator.ReserveIrp, IoSizeOfIrp(StackSize), StackSize);
+
+    /* And return it to the caller */
+    return IopReserveIrpAllocator.ReserveIrp;
+}
+
+VOID
+IopFreeReserveIrp(IN CCHAR PriorityBoost)
+{
+    /* Mark we don't use the IRP anymore */
+    InterlockedExchange(&IopReserveIrpAllocator.ReserveIrpInUse, 0);
+
+    /* And set the event if someone is waiting on the IRP */
+    KeSetEvent(&IopReserveIrpAllocator.WaitEvent, PriorityBoost, FALSE);
+}
+
 /* FUNCTIONS *****************************************************************/
 
 /*
@@ -1423,7 +1486,21 @@ IofCompleteRequest(IN PIRP Irp,
             KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
 
             /* Free the IRP for a Paging I/O Only, Close is handled by us */
-            if (Flags) IoFreeIrp(Irp);
+            if (Flags)
+            {
+                /* If we were using the reserve IRP, then call the appropriate
+                 * free function (to make the IRP available again)
+                 */
+                if (Irp == IopReserveIrpAllocator.ReserveIrp)
+                {
+                    IopFreeReserveIrp(PriorityBoost);
+                }
+                /* Otherwise, free for real! */
+                else
+                {
+                    IoFreeIrp(Irp);
+                }
+            }
         }
         else
         {