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