xref: /reactos/ntoskrnl/fsrtl/stackovf.c (revision 7eead935)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/fsrtl/stackovf.c
5  * PURPOSE:         Provides Stack Overflow support for File System Drivers
6  * PROGRAMMERS:     Pierre Schweitzer (pierre@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 /* We have one queue for paging files, one queue for normal files
18  * Queue 0 is for non-paging files
19  * Queue 1 is for paging files
20  * Don't add new/change current queues unless you know what you do
21  * Most of the code relies on the fact that we have two queues in that order
22  */
23 #define FSRTLP_MAX_QUEUES 2
24 
25 typedef struct _STACK_OVERFLOW_WORK_ITEM
26 {
27 
28     WORK_QUEUE_ITEM WorkItem;
29     PFSRTL_STACK_OVERFLOW_ROUTINE Routine;
30     PVOID Context;
31     PKEVENT Event;
32 } STACK_OVERFLOW_WORK_ITEM, *PSTACK_OVERFLOW_WORK_ITEM;
33 
34 KEVENT StackOverflowFallbackSerialEvent;
35 STACK_OVERFLOW_WORK_ITEM StackOverflowFallback;
36 KQUEUE FsRtlWorkerQueues[FSRTLP_MAX_QUEUES];
37 
38 /* PRIVATE FUNCTIONS *********************************************************/
39 
40 /*
41  * @implemented
42  */
43 VOID
44 NTAPI
45 FsRtlStackOverflowRead(IN PVOID Context)
46 {
47     PSTACK_OVERFLOW_WORK_ITEM WorkItem;
48 
49     WorkItem = (PSTACK_OVERFLOW_WORK_ITEM)Context;
50 
51     /* Put us as top IRP for current thread */
52     IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
53     /* And call FsRtlSORoutine */
54     WorkItem->Routine(WorkItem->Context, WorkItem->Event);
55 
56     /* If we were using fallback workitem, don't free it, just reset event */
57     if (WorkItem == &StackOverflowFallback)
58     {
59         KeSetEvent(&StackOverflowFallbackSerialEvent, 0, FALSE);
60     }
61     /* Otherwise, free the work item */
62     else
63     {
64         ExFreePoolWithTag(WorkItem, 'FSrs');
65     }
66 
67     /* Reset top level */
68     IoSetTopLevelIrp(NULL);
69 }
70 
71 /*
72  * @implemented
73  */
74 VOID
75 NTAPI
76 FsRtlpPostStackOverflow(IN PVOID Context,
77                         IN PKEVENT Event,
78                         IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine,
79                         IN BOOLEAN IsPaging)
80 {
81     PSTACK_OVERFLOW_WORK_ITEM WorkItem;
82 
83     /* Try to allocate a work item */
84     WorkItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(STACK_OVERFLOW_WORK_ITEM), 'FSrs');
85     if (WorkItem == NULL)
86     {
87         /* If we failed, and we are not a paging file, just raise an error */
88         if (!IsPaging)
89         {
90             RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
91         }
92 
93         /* Otherwise, wait for fallback workitem to be available and use it */
94         KeWaitForSingleObject(&StackOverflowFallbackSerialEvent, Executive, KernelMode, FALSE, NULL);
95         WorkItem = &StackOverflowFallback;
96     }
97 
98     /* Initialize work item */
99     WorkItem->Context = Context;
100     WorkItem->Event = Event;
101     WorkItem->Routine = StackOverflowRoutine;
102     ExInitializeWorkItem(&WorkItem->WorkItem, FsRtlStackOverflowRead, WorkItem);
103 
104     /* And queue it in the appropriate queue (paging or not?) */
105     KeInsertQueue(&FsRtlWorkerQueues[IsPaging], &WorkItem->WorkItem.List);
106 }
107 
108 /*
109  * @implemented
110  */
111 VOID
112 NTAPI
113 FsRtlWorkerThread(IN PVOID StartContext)
114 {
115     KIRQL Irql;
116     PLIST_ENTRY Entry;
117     PWORK_QUEUE_ITEM WorkItem;
118     ULONG_PTR QueueId = (ULONG_PTR)StartContext;
119 
120     /* Set our priority according to the queue we're dealing with */
121     KeSetPriorityThread(&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + QueueId);
122 
123     /* Loop for events */
124     for (;;)
125     {
126         /* Look for next event */
127         Entry = KeRemoveQueue(&FsRtlWorkerQueues[QueueId], KernelMode, NULL);
128         WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
129 
130         /* Call its routine (here: FsRtlStackOverflowRead) */
131         WorkItem->WorkerRoutine(WorkItem->Parameter);
132 
133         /* Check we're still at passive level or bugcheck */
134         Irql = KeGetCurrentIrql();
135         if (Irql != PASSIVE_LEVEL)
136         {
137             KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine,
138                          (ULONG_PTR)Irql, (ULONG_PTR)WorkItem->WorkerRoutine,
139                          (ULONG_PTR)WorkItem);
140         }
141     }
142 }
143 
144 /*
145  * @implemented
146  */
147 INIT_FUNCTION
148 NTSTATUS
149 NTAPI
150 FsRtlInitializeWorkerThread(VOID)
151 {
152     ULONG_PTR i;
153     NTSTATUS Status;
154     HANDLE ThreadHandle;
155     OBJECT_ATTRIBUTES ObjectAttributes;
156 
157     /* Initialize each queue we have */
158     for (i = 0; i < FSRTLP_MAX_QUEUES; ++i)
159     {
160         InitializeObjectAttributes(&ObjectAttributes,
161                                    NULL,
162                                    0,
163                                    NULL,
164                                    NULL);
165 
166         /* Initialize the queue and its associated thread and pass it the queue ID */
167         KeInitializeQueue(&FsRtlWorkerQueues[i], 0);
168         Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes,
169                                       0, 0, FsRtlWorkerThread, (PVOID)i);
170         if (!NT_SUCCESS(Status))
171         {
172             return Status;
173         }
174 
175         /* Don't leak handle */
176         ZwClose(ThreadHandle);
177     }
178 
179     /* Also initialize our fallback event, set it to ensure it's already usable */
180     KeInitializeEvent(&StackOverflowFallbackSerialEvent, SynchronizationEvent, TRUE);
181 
182     return Status;
183 }
184 
185 /* PUBLIC FUNCTIONS **********************************************************/
186 
187 /*++
188  * @name FsRtlPostPagingFileStackOverflow
189  * @implemented NT 5.2
190  *
191  *     The FsRtlPostPagingFileStackOverflow routine
192  *
193  * @param Context
194  *
195  * @param Event
196  *
197  * @param StackOverflowRoutine
198  *
199  * @return
200  *
201  * @remarks None.
202  *
203  *--*/
204 VOID
205 NTAPI
206 FsRtlPostPagingFileStackOverflow(IN PVOID Context,
207                                  IN PKEVENT Event,
208                                  IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine)
209 {
210     FsRtlpPostStackOverflow(Context, Event, StackOverflowRoutine, TRUE);
211 }
212 
213 /*++
214  * @name FsRtlPostStackOverflow
215  * @implemented NT 5.2
216  *
217  *     The FsRtlPostStackOverflow routine
218  *
219  * @param Context
220  *
221  * @param Event
222  *
223  * @param StackOverflowRoutine
224  *
225  * @return
226  *
227  * @remarks None.
228  *
229  *--*/
230 VOID
231 NTAPI
232 FsRtlPostStackOverflow(IN PVOID Context,
233                        IN PKEVENT Event,
234                        IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine)
235 {
236     FsRtlpPostStackOverflow(Context, Event, StackOverflowRoutine, FALSE);
237 }
238 
239 /* EOF */
240