[CRT] Massively improve performance of rand_s
[reactos.git] / ntoskrnl / config / cmlazy.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PURPOSE: Configuration Manager - Internal Registry APIs
5 * PROGRAMMERS: Alex Ionescu <alex.ionescu@reactos.org>
6 */
7
8 /* INCLUDES *******************************************************************/
9
10 #include "ntoskrnl.h"
11 #define NDEBUG
12 #include "debug.h"
13
14 /* GLOBALS ********************************************************************/
15
16 KTIMER CmpLazyFlushTimer;
17 KDPC CmpLazyFlushDpc;
18 WORK_QUEUE_ITEM CmpLazyWorkItem;
19 KTIMER CmpEnableLazyFlushTimer;
20 KDPC CmpEnableLazyFlushDpc;
21 BOOLEAN CmpLazyFlushPending;
22 BOOLEAN CmpForceForceFlush;
23 BOOLEAN CmpHoldLazyFlush = TRUE;
24 ULONG CmpLazyFlushIntervalInSeconds = 5;
25 static ULONG CmpLazyFlushHiveCount = 7;
26 ULONG CmpLazyFlushCount = 1;
27 LONG CmpFlushStarveWriters;
28
29 /* FUNCTIONS ******************************************************************/
30
31 BOOLEAN
32 NTAPI
33 CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush,
34 _Out_ PBOOLEAN Error,
35 _Out_ PULONG DirtyCount)
36 {
37 NTSTATUS Status;
38 PLIST_ENTRY NextEntry;
39 PCMHIVE CmHive;
40 BOOLEAN Result;
41 ULONG HiveCount = CmpLazyFlushHiveCount;
42
43 /* Set Defaults */
44 *Error = FALSE;
45 *DirtyCount = 0;
46
47 /* Don't do anything if we're not supposed to */
48 if (CmpNoWrite) return TRUE;
49
50 /* Make sure we have to flush at least one hive */
51 if (!HiveCount) HiveCount = 1;
52
53 /* Acquire the list lock and loop */
54 ExAcquirePushLockShared(&CmpHiveListHeadLock);
55 NextEntry = CmpHiveListHead.Flink;
56 while ((NextEntry != &CmpHiveListHead) && HiveCount)
57 {
58 /* Get the hive and check if we should flush it */
59 CmHive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
60 if (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH) &&
61 (CmHive->FlushCount != CmpLazyFlushCount))
62 {
63 /* Great sucess! */
64 Result = TRUE;
65
66 /* One less to flush */
67 HiveCount--;
68
69 /* Ignore clean or volatile hives */
70 if ((!CmHive->Hive.DirtyCount && !ForceFlush) ||
71 (CmHive->Hive.HiveFlags & HIVE_VOLATILE))
72 {
73 /* Don't do anything but do update the count */
74 CmHive->FlushCount = CmpLazyFlushCount;
75 DPRINT("Hive %wZ is clean.\n", &CmHive->FileFullPath);
76 }
77 else
78 {
79 /* Do the sync */
80 DPRINT("Flushing: %wZ\n", &CmHive->FileFullPath);
81 DPRINT("Handle: %p\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]);
82 Status = HvSyncHive(&CmHive->Hive);
83 if(!NT_SUCCESS(Status))
84 {
85 /* Let them know we failed */
86 DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n",
87 &CmHive->FileFullPath, CmHive->FileHandles[HFILE_TYPE_PRIMARY], Status);
88 *Error = TRUE;
89 Result = FALSE;
90 break;
91 }
92 CmHive->FlushCount = CmpLazyFlushCount;
93 }
94 }
95 else if (CmHive->Hive.DirtyCount &&
96 !(CmHive->Hive.HiveFlags & HIVE_VOLATILE) &&
97 !(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
98 {
99 /* Use another lazy flusher for this hive */
100 ASSERT(CmHive->FlushCount == CmpLazyFlushCount);
101 *DirtyCount += CmHive->Hive.DirtyCount;
102 DPRINT("CmHive %wZ already uptodate.\n", &CmHive->FileFullPath);
103 }
104
105 /* Try the next one */
106 NextEntry = NextEntry->Flink;
107 }
108
109 /* Check if we've flushed everything */
110 if (NextEntry == &CmpHiveListHead)
111 {
112 /* We have, tell the caller we're done */
113 Result = FALSE;
114 }
115 else
116 {
117 /* We need to be called again */
118 Result = TRUE;
119 }
120
121 /* Unlock the list and return the result */
122 ExReleasePushLock(&CmpHiveListHeadLock);
123 return Result;
124 }
125
126 _Function_class_(KDEFERRED_ROUTINE)
127 VOID
128 NTAPI
129 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
130 IN PVOID DeferredContext,
131 IN PVOID SystemArgument1,
132 IN PVOID SystemArgument2)
133 {
134 /* Don't stop lazy flushing from happening anymore */
135 CmpHoldLazyFlush = FALSE;
136 }
137
138 _Function_class_(KDEFERRED_ROUTINE)
139 VOID
140 NTAPI
141 CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
142 IN PVOID DeferredContext,
143 IN PVOID SystemArgument1,
144 IN PVOID SystemArgument2)
145 {
146 /* Check if we should queue the lazy flush worker */
147 DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending ? "yes" : "no", CmpHoldLazyFlush ? "yes" : "no");
148 if (!CmpLazyFlushPending && !CmpHoldLazyFlush)
149 {
150 CmpLazyFlushPending = TRUE;
151 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
152 }
153 }
154
155 VOID
156 NTAPI
157 CmpLazyFlush(VOID)
158 {
159 LARGE_INTEGER DueTime;
160 PAGED_CODE();
161
162 /* Check if we should set the lazy flush timer */
163 if (!CmpNoWrite && !CmpHoldLazyFlush)
164 {
165 /* Do it */
166 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
167 -10 * 1000 * 1000);
168 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
169 }
170 }
171
172 _Function_class_(WORKER_THREAD_ROUTINE)
173 VOID
174 NTAPI
175 CmpLazyFlushWorker(IN PVOID Parameter)
176 {
177 BOOLEAN ForceFlush, Result, MoreWork = FALSE;
178 ULONG DirtyCount = 0;
179 PAGED_CODE();
180
181 /* Don't do anything if lazy flushing isn't enabled yet */
182 if (CmpHoldLazyFlush)
183 {
184 DPRINT1("Lazy flush held. Bye bye.\n");
185 CmpLazyFlushPending = FALSE;
186 return;
187 }
188
189 /* Check if we are forcing a flush */
190 ForceFlush = CmpForceForceFlush;
191 if (ForceFlush)
192 {
193 DPRINT("Forcing flush.\n");
194 /* Lock the registry exclusively */
195 CmpLockRegistryExclusive();
196 }
197 else
198 {
199 DPRINT("Not forcing flush.\n");
200 /* Starve writers before locking */
201 InterlockedIncrement(&CmpFlushStarveWriters);
202 CmpLockRegistry();
203 }
204
205 /* Flush the next hive */
206 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
207 if (!MoreWork)
208 {
209 /* We're done */
210 InterlockedIncrement((PLONG)&CmpLazyFlushCount);
211 }
212
213 /* Check if we have starved writers */
214 if (!ForceFlush)
215 InterlockedDecrement(&CmpFlushStarveWriters);
216
217 /* Not pending anymore, release the registry lock */
218 CmpLazyFlushPending = FALSE;
219 CmpUnlockRegistry();
220
221 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
222 MoreWork ? "Yes" : "No", DirtyCount);
223
224 if (MoreWork)
225 {
226 /* Relaunch the flush timer, so the remaining hives get flushed */
227 CmpLazyFlush();
228 }
229 }
230
231 VOID
232 NTAPI
233 CmpCmdInit(IN BOOLEAN SetupBoot)
234 {
235 LARGE_INTEGER DueTime;
236 PAGED_CODE();
237
238 /* Setup the lazy DPC */
239 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
240
241 /* Setup the lazy timer */
242 KeInitializeTimer(&CmpLazyFlushTimer);
243
244 /* Setup the lazy worker */
245 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
246
247 /* Setup the forced-lazy DPC and timer */
248 KeInitializeDpc(&CmpEnableLazyFlushDpc,
249 CmpEnableLazyFlushDpcRoutine,
250 NULL);
251 KeInitializeTimer(&CmpEnableLazyFlushTimer);
252
253 /* Enable lazy flushing after 10 minutes */
254 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
255 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
256
257 /* Setup flush variables */
258 CmpNoWrite = CmpMiniNTBoot;
259 CmpWasSetupBoot = SetupBoot;
260
261 /* Testing: Force Lazy Flushing */
262 CmpHoldLazyFlush = FALSE;
263
264 /* Setup the system hives list if this is not a Setup boot */
265 if (!SetupBoot)
266 CmpInitializeHiveList();
267
268 /* Now that the system hives are loaded, if we are in PE mode,
269 * all other hives will be loaded with full access */
270 if (CmpMiniNTBoot)
271 CmpShareSystemHives = FALSE;
272 }
273
274 NTSTATUS
275 NTAPI
276 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
277 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
278 IN OUT PBOOLEAN Allocate,
279 OUT PCMHIVE *NewHive,
280 IN ULONG CheckFlags)
281 {
282 NTSTATUS Status;
283 UNICODE_STRING FileName;
284 PWCHAR FilePath;
285 ULONG Length;
286 OBJECT_NAME_INFORMATION DummyNameInfo;
287 POBJECT_NAME_INFORMATION FileNameInfo;
288
289 PAGED_CODE();
290
291 if (FileAttributes->RootDirectory)
292 {
293 /*
294 * Validity check: The ObjectName is relative to RootDirectory,
295 * therefore it must not start with a path separator.
296 */
297 if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer &&
298 FileAttributes->ObjectName->Length >= sizeof(WCHAR) &&
299 *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR)
300 {
301 return STATUS_OBJECT_PATH_SYNTAX_BAD;
302 }
303
304 /* Determine the right buffer size and allocate */
305 Status = ZwQueryObject(FileAttributes->RootDirectory,
306 ObjectNameInformation,
307 &DummyNameInfo,
308 sizeof(DummyNameInfo),
309 &Length);
310 if (Status != STATUS_BUFFER_OVERFLOW)
311 {
312 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status);
313 return Status;
314 }
315
316 FileNameInfo = ExAllocatePoolWithTag(PagedPool,
317 Length + sizeof(UNICODE_NULL),
318 TAG_CM);
319 if (FileNameInfo == NULL)
320 {
321 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
322 return STATUS_INSUFFICIENT_RESOURCES;
323 }
324
325 /* Try to get the value */
326 Status = ZwQueryObject(FileAttributes->RootDirectory,
327 ObjectNameInformation,
328 FileNameInfo,
329 Length,
330 &Length);
331 if (!NT_SUCCESS(Status))
332 {
333 /* Fail */
334 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status);
335 ExFreePoolWithTag(FileNameInfo, TAG_CM);
336 return Status;
337 }
338
339 /* Null-terminate and add the length of the terminator */
340 Length -= sizeof(OBJECT_NAME_INFORMATION);
341 FilePath = FileNameInfo->Name.Buffer;
342 FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL;
343 Length += sizeof(UNICODE_NULL);
344
345 /* Compute the size of the full path; Length already counts the terminating NULL */
346 Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length;
347 if (Length > MAXUSHORT)
348 {
349 /* Name size too long, bail out */
350 ExFreePoolWithTag(FileNameInfo, TAG_CM);
351 return STATUS_OBJECT_PATH_INVALID;
352 }
353
354 /* Build the full path */
355 RtlInitEmptyUnicodeString(&FileName, NULL, 0);
356 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
357 if (!FileName.Buffer)
358 {
359 /* Fail */
360 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
361 ExFreePoolWithTag(FileNameInfo, TAG_CM);
362 return STATUS_INSUFFICIENT_RESOURCES;
363 }
364 FileName.MaximumLength = Length;
365 RtlCopyUnicodeString(&FileName, &FileNameInfo->Name);
366 ExFreePoolWithTag(FileNameInfo, TAG_CM);
367
368 /*
369 * Append a path terminator if needed (we have already accounted
370 * for a possible extra one when allocating the buffer).
371 */
372 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
373 FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR)
374 {
375 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */
376 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
377 FileName.Length += sizeof(WCHAR);
378 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL;
379 }
380
381 /* Append the object name */
382 Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName);
383 if (!NT_SUCCESS(Status))
384 {
385 /* Fail */
386 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status);
387 ExFreePoolWithTag(FileName.Buffer, TAG_CM);
388 return Status;
389 }
390 }
391 else
392 {
393 FileName = *FileAttributes->ObjectName;
394 }
395
396 /* Open the file in the current security context */
397 Status = CmpInitHiveFromFile(&FileName,
398 0,
399 NewHive,
400 Allocate,
401 CheckFlags);
402 if (((Status == STATUS_ACCESS_DENIED) ||
403 (Status == STATUS_NO_SUCH_USER) ||
404 (Status == STATUS_WRONG_PASSWORD) ||
405 (Status == STATUS_ACCOUNT_EXPIRED) ||
406 (Status == STATUS_ACCOUNT_DISABLED) ||
407 (Status == STATUS_ACCOUNT_RESTRICTION)) &&
408 ImpersonationContext)
409 {
410 /* We failed due to an account/security error, impersonate SYSTEM */
411 Status = SeImpersonateClientEx(ImpersonationContext, NULL);
412 if (NT_SUCCESS(Status))
413 {
414 /* Now try again */
415 Status = CmpInitHiveFromFile(&FileName,
416 0,
417 NewHive,
418 Allocate,
419 CheckFlags);
420
421 /* Restore impersonation token */
422 PsRevertToSelf();
423 }
424 }
425
426 if (FileAttributes->RootDirectory)
427 {
428 ExFreePoolWithTag(FileName.Buffer, TAG_CM);
429 }
430
431 /* Return status of open attempt */
432 return Status;
433 }
434
435 VOID
436 NTAPI
437 CmpShutdownWorkers(VOID)
438 {
439 /* Stop lazy flushing */
440 PAGED_CODE();
441 KeCancelTimer(&CmpLazyFlushTimer);
442 }
443
444 VOID
445 NTAPI
446 CmSetLazyFlushState(IN BOOLEAN Enable)
447 {
448 /* Set state for lazy flusher */
449 CmpHoldLazyFlush = !Enable;
450 }
451
452 /* EOF */