0231c627ce8fcde73c2a736aaad6c9d330251292
[reactos.git] / reactos / ntoskrnl / io / errlog.c
1 /* $Id: errlog.c,v 1.19 2004/08/21 20:51:26 tamlin Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/io/errlog.c
6 * PURPOSE: Error logging
7 * PROGRAMMER: David Welch (welch@cwcom.net)
8 * UPDATE HISTORY:
9 * Created 22/05/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* TYPES *********************************************************************/
19
20 typedef struct _ERROR_LOG_ENTRY
21 {
22 LIST_ENTRY Entry;
23 LARGE_INTEGER TimeStamp;
24 PVOID IoObject;
25 ULONG PacketSize;
26 } ERROR_LOG_ENTRY, *PERROR_LOG_ENTRY;
27
28 typedef struct _LOG_WORKER_DPC
29 {
30 KDPC Dpc;
31 KTIMER Timer;
32 } LOG_WORKER_DPC, *PLOG_WORKER_DPC;
33
34
35 static VOID STDCALL
36 IopLogWorker (PVOID Parameter);
37
38
39 /* GLOBALS *******************************************************************/
40
41 static KSPIN_LOCK IopAllocationLock;
42 static ULONG IopTotalLogSize;
43
44 static KSPIN_LOCK IopLogListLock;
45 static LIST_ENTRY IopLogListHead;
46
47 static BOOLEAN IopLogWorkerRunning = FALSE;
48 static BOOLEAN IopLogPortConnected = FALSE;
49 static HANDLE IopLogPort;
50
51
52 /* FUNCTIONS *****************************************************************/
53
54 NTSTATUS
55 IopInitErrorLog (VOID)
56 {
57 IopTotalLogSize = 0;
58 KeInitializeSpinLock (&IopAllocationLock);
59
60 KeInitializeSpinLock (&IopLogListLock);
61 InitializeListHead (&IopLogListHead);
62
63 return STATUS_SUCCESS;
64 }
65
66
67 static VOID STDCALL
68 IopLogDpcRoutine (PKDPC Dpc,
69 PVOID DeferredContext,
70 PVOID SystemArgument1,
71 PVOID SystemArgument2)
72 {
73 PWORK_QUEUE_ITEM LogWorkItem;
74
75 DPRINT ("\nIopLogDpcRoutine() called\n");
76
77 /* Release the WorkerDpc struct */
78 ExFreePool (DeferredContext);
79
80 /* Allocate, initialize and restart a work item */
81 LogWorkItem = ExAllocatePool (NonPagedPool,
82 sizeof(WORK_QUEUE_ITEM));
83 if (LogWorkItem == NULL)
84 {
85 IopLogWorkerRunning = FALSE;
86 return;
87 }
88
89 ExInitializeWorkItem (LogWorkItem,
90 IopLogWorker,
91 LogWorkItem);
92
93 ExQueueWorkItem (LogWorkItem,
94 DelayedWorkQueue);
95 }
96
97
98 static VOID
99 IopRestartLogWorker (VOID)
100 {
101 PLOG_WORKER_DPC WorkerDpc;
102 LARGE_INTEGER Timeout;
103
104 DPRINT ("IopRestartWorker() called\n");
105
106 WorkerDpc = ExAllocatePool (NonPagedPool,
107 sizeof(LOG_WORKER_DPC));
108 if (WorkerDpc == NULL)
109 {
110 IopLogWorkerRunning = FALSE;
111 return;
112 }
113
114 /* Initialize DPC and Timer */
115 KeInitializeDpc (&WorkerDpc->Dpc,
116 IopLogDpcRoutine,
117 WorkerDpc);
118 KeInitializeTimer (&WorkerDpc->Timer);
119
120 /* Restart after 30 seconds */
121 #if defined(__GNUC__)
122 Timeout.QuadPart = -300000000LL;
123 #else
124 Timeout.QuadPart = -300000000;
125 #endif
126 KeSetTimer (&WorkerDpc->Timer,
127 Timeout,
128 &WorkerDpc->Dpc);
129 }
130
131
132 static BOOLEAN
133 IopConnectLogPort (VOID)
134 {
135 UNICODE_STRING PortName;
136 NTSTATUS Status;
137
138 DPRINT ("IopConnectLogPort() called\n");
139
140 RtlInitUnicodeString (&PortName,
141 L"\\ErrorLogPort");
142
143 Status = NtConnectPort (&IopLogPort,
144 &PortName,
145 NULL,
146 NULL,
147 NULL,
148 NULL,
149 NULL,
150 NULL);
151 if (!NT_SUCCESS(Status))
152 {
153 DPRINT ("NtConnectPort() failed (Status %lx)\n", Status);
154 return FALSE;
155 }
156
157 DPRINT ("IopConnectLogPort() done\n");
158
159 return TRUE;
160 }
161
162
163 static VOID STDCALL
164 IopLogWorker (PVOID Parameter)
165 {
166 PERROR_LOG_ENTRY LogEntry;
167 PLPC_MAX_MESSAGE Request;
168 PIO_ERROR_LOG_MESSAGE Message;
169 PIO_ERROR_LOG_PACKET Packet;
170 KIRQL Irql;
171 NTSTATUS Status;
172
173 UCHAR Buffer[256];
174 POBJECT_NAME_INFORMATION ObjectNameInfo;
175 ULONG ReturnedLength;
176 PWCHAR DriverName;
177 ULONG DriverNameLength;
178
179 DPRINT ("IopLogWorker() called\n");
180
181 /* Release the work item */
182 ExFreePool (Parameter);
183
184
185 /* Connect to the error log port */
186 if (IopLogPortConnected == FALSE)
187 {
188 if (IopConnectLogPort () == FALSE)
189 {
190 IopRestartLogWorker ();
191 return;
192 }
193
194 IopLogPortConnected = TRUE;
195 }
196
197 while (TRUE)
198 {
199 /* Remove last entry from the list */
200 KeAcquireSpinLock (&IopLogListLock,
201 &Irql);
202
203 if (!IsListEmpty (&IopLogListHead))
204 {
205 LogEntry = CONTAINING_RECORD (IopLogListHead.Blink,
206 ERROR_LOG_ENTRY,
207 Entry);
208 RemoveEntryList (&LogEntry->Entry);
209 }
210 else
211 {
212 LogEntry = NULL;
213 }
214
215 KeReleaseSpinLock (&IopLogListLock,
216 Irql);
217
218 if (LogEntry == NULL)
219 {
220 DPRINT ("No message in log list\n");
221 break;
222 }
223
224 /* Get pointer to the log packet */
225 Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
226
227
228 /* Get driver or device name */
229 ObjectNameInfo = (POBJECT_NAME_INFORMATION)Buffer;
230 Status = ObQueryNameString (LogEntry->IoObject,
231 ObjectNameInfo,
232 256,
233 &ReturnedLength);
234 if (NT_SUCCESS(Status))
235 {
236 DPRINT ("ReturnedLength: %lu\n", ReturnedLength);
237 DPRINT ("Length: %hu\n", ObjectNameInfo->Name.Length);
238 DPRINT ("MaximumLength: %hu\n", ObjectNameInfo->Name.MaximumLength);
239 DPRINT ("Object: %wZ\n", &ObjectNameInfo->Name);
240
241 DriverName = wcsrchr(ObjectNameInfo->Name.Buffer, L'\\');
242 if (DriverName != NULL)
243 DriverName++;
244 else
245 DriverName = ObjectNameInfo->Name.Buffer;
246
247 DriverNameLength = wcslen (DriverName) * sizeof(WCHAR);
248 DPRINT ("Driver name '%S'\n", DriverName);
249 }
250 else
251 {
252 DriverName = NULL;
253 DriverNameLength = 0;
254 }
255
256 /* Allocate request buffer */
257 Request = ExAllocatePool (NonPagedPool,
258 sizeof(LPC_MAX_MESSAGE));
259 if (Request == NULL)
260 {
261 DPRINT ("Failed to allocate request buffer!\n");
262
263 /* Requeue log message and restart the worker */
264 ExInterlockedInsertTailList (&IopLogListHead,
265 &LogEntry->Entry,
266 &IopLogListLock);
267 IopRestartLogWorker ();
268
269 return;
270 }
271
272 /* Initialize the log message */
273 Message = (PIO_ERROR_LOG_MESSAGE)Request->Data;
274 Message->Type = 0xC; //IO_TYPE_ERROR_MESSAGE;
275 Message->Size =
276 sizeof(IO_ERROR_LOG_MESSAGE) - sizeof(IO_ERROR_LOG_PACKET) +
277 LogEntry->PacketSize + DriverNameLength;
278 Message->DriverNameLength = (USHORT)DriverNameLength;
279 Message->TimeStamp.QuadPart = LogEntry->TimeStamp.QuadPart;
280 Message->DriverNameOffset = (DriverName != NULL) ? LogEntry->PacketSize : 0;
281
282 /* Copy error log packet */
283 RtlCopyMemory (&Message->EntryData,
284 Packet,
285 LogEntry->PacketSize);
286
287 /* Copy driver or device name */
288 RtlCopyMemory ((PVOID)((ULONG_PTR)Message + Message->DriverNameOffset),
289 DriverName,
290 DriverNameLength);
291
292 DPRINT ("SequenceNumber %lx\n", Packet->SequenceNumber);
293
294 Request->Header.DataSize = Message->Size;
295 Request->Header.MessageSize =
296 Request->Header.DataSize + sizeof(LPC_MESSAGE);
297
298 /* Send the error message to the log port */
299 Status = NtRequestPort (IopLogPort,
300 &Request->Header);
301
302 /* Release request buffer */
303 ExFreePool (Request);
304
305 if (!NT_SUCCESS(Status))
306 {
307 DPRINT ("NtRequestPort() failed (Status %lx)\n", Status);
308
309 /* Requeue log message and restart the worker */
310 ExInterlockedInsertTailList (&IopLogListHead,
311 &LogEntry->Entry,
312 &IopLogListLock);
313 IopRestartLogWorker ();
314
315 return;
316 }
317
318 /* Release error log entry */
319 KeAcquireSpinLock (&IopAllocationLock,
320 &Irql);
321
322 IopTotalLogSize -= LogEntry->PacketSize;
323 ExFreePool (LogEntry);
324
325 KeReleaseSpinLock (&IopAllocationLock,
326 Irql);
327 }
328
329 IopLogWorkerRunning = FALSE;
330
331 DPRINT ("IopLogWorker() done\n");
332 }
333
334
335 /*
336 * @implemented
337 */
338 PVOID STDCALL
339 IoAllocateErrorLogEntry (IN PVOID IoObject,
340 IN UCHAR EntrySize)
341 {
342 PERROR_LOG_ENTRY LogEntry;
343 ULONG LogEntrySize;
344 KIRQL Irql;
345
346 DPRINT1 ("IoAllocateErrorLogEntry() called\n");
347
348 if (IoObject == NULL)
349 return NULL;
350
351 KeAcquireSpinLock (&IopAllocationLock,
352 &Irql);
353
354 if (IopTotalLogSize > PAGE_SIZE)
355 {
356 KeReleaseSpinLock (&IopAllocationLock,
357 Irql);
358 return NULL;
359 }
360
361 LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize;
362 LogEntry = ExAllocatePool (NonPagedPool,
363 LogEntrySize);
364 if (LogEntry == NULL)
365 {
366 KeReleaseSpinLock (&IopAllocationLock,
367 Irql);
368 return NULL;
369 }
370
371 IopTotalLogSize += EntrySize;
372
373 LogEntry->IoObject = IoObject;
374 LogEntry->PacketSize = LogEntrySize;
375
376 KeReleaseSpinLock (&IopAllocationLock,
377 Irql);
378
379 return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
380 }
381
382 /*
383 * @unimplemented
384 */
385 VOID
386 STDCALL
387 IoFreeErrorLogEntry(
388 PVOID ElEntry
389 )
390 {
391 UNIMPLEMENTED;
392 }
393
394 /*
395 * @implemented
396 */
397 VOID STDCALL
398 IoWriteErrorLogEntry (IN PVOID ElEntry)
399 {
400 PWORK_QUEUE_ITEM LogWorkItem;
401 PERROR_LOG_ENTRY LogEntry;
402 KIRQL Irql;
403
404 DPRINT ("IoWriteErrorLogEntry() called\n");
405
406 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
407
408 /* Get time stamp */
409 KeQuerySystemTime (&LogEntry->TimeStamp);
410
411 KeAcquireSpinLock (&IopLogListLock,
412 &Irql);
413
414 InsertHeadList (&IopLogListHead,
415 &LogEntry->Entry);
416
417 if (IopLogWorkerRunning == FALSE)
418 {
419 LogWorkItem = ExAllocatePool (NonPagedPool,
420 sizeof(WORK_QUEUE_ITEM));
421 if (LogWorkItem != NULL)
422 {
423 ExInitializeWorkItem (LogWorkItem,
424 IopLogWorker,
425 LogWorkItem);
426
427 ExQueueWorkItem (LogWorkItem,
428 DelayedWorkQueue);
429
430 IopLogWorkerRunning = TRUE;
431 }
432 }
433
434 KeReleaseSpinLock (&IopLogListLock,
435 Irql);
436
437 DPRINT ("IoWriteErrorLogEntry() done\n");
438 }
439
440 /* EOF */