- Implement IoFreeErrorLogEntry().
[reactos.git] / reactos / ntoskrnl / io / errlog.c
1 /* $Id: errlog.c,v 1.20 2004/09/28 12:51:14 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 <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 Timeout.QuadPart = (LONGLONG)-300000000;
122 KeSetTimer (&WorkerDpc->Timer,
123 Timeout,
124 &WorkerDpc->Dpc);
125 }
126
127
128 static BOOLEAN
129 IopConnectLogPort (VOID)
130 {
131 UNICODE_STRING PortName;
132 NTSTATUS Status;
133
134 DPRINT ("IopConnectLogPort() called\n");
135
136 RtlInitUnicodeString (&PortName,
137 L"\\ErrorLogPort");
138
139 Status = NtConnectPort (&IopLogPort,
140 &PortName,
141 NULL,
142 NULL,
143 NULL,
144 NULL,
145 NULL,
146 NULL);
147 if (!NT_SUCCESS(Status))
148 {
149 DPRINT ("NtConnectPort() failed (Status %lx)\n", Status);
150 return FALSE;
151 }
152
153 DPRINT ("IopConnectLogPort() done\n");
154
155 return TRUE;
156 }
157
158
159 static VOID STDCALL
160 IopLogWorker (PVOID Parameter)
161 {
162 PERROR_LOG_ENTRY LogEntry;
163 PLPC_MAX_MESSAGE Request;
164 PIO_ERROR_LOG_MESSAGE Message;
165 PIO_ERROR_LOG_PACKET Packet;
166 KIRQL Irql;
167 NTSTATUS Status;
168
169 UCHAR Buffer[256];
170 POBJECT_NAME_INFORMATION ObjectNameInfo;
171 ULONG ReturnedLength;
172 PWCHAR DriverName;
173 ULONG DriverNameLength;
174
175 DPRINT ("IopLogWorker() called\n");
176
177 /* Release the work item */
178 ExFreePool (Parameter);
179
180 /* Connect to the error log port */
181 if (IopLogPortConnected == FALSE)
182 {
183 if (IopConnectLogPort () == FALSE)
184 {
185 IopRestartLogWorker ();
186 return;
187 }
188
189 IopLogPortConnected = TRUE;
190 }
191
192 while (TRUE)
193 {
194 /* Remove last entry from the list */
195 KeAcquireSpinLock (&IopLogListLock,
196 &Irql);
197
198 if (!IsListEmpty (&IopLogListHead))
199 {
200 LogEntry = CONTAINING_RECORD (IopLogListHead.Blink,
201 ERROR_LOG_ENTRY,
202 Entry);
203 RemoveEntryList (&LogEntry->Entry);
204 }
205 else
206 {
207 LogEntry = NULL;
208 }
209
210 KeReleaseSpinLock (&IopLogListLock,
211 Irql);
212
213 if (LogEntry == NULL)
214 {
215 DPRINT ("No message in log list\n");
216 break;
217 }
218
219 /* Get pointer to the log packet */
220 Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
221
222
223 /* Get driver or device name */
224 ObjectNameInfo = (POBJECT_NAME_INFORMATION)Buffer;
225 Status = ObQueryNameString (LogEntry->IoObject,
226 ObjectNameInfo,
227 256,
228 &ReturnedLength);
229 if (NT_SUCCESS(Status))
230 {
231 DPRINT ("ReturnedLength: %lu\n", ReturnedLength);
232 DPRINT ("Length: %hu\n", ObjectNameInfo->Name.Length);
233 DPRINT ("MaximumLength: %hu\n", ObjectNameInfo->Name.MaximumLength);
234 DPRINT ("Object: %wZ\n", &ObjectNameInfo->Name);
235
236 DriverName = wcsrchr(ObjectNameInfo->Name.Buffer, L'\\');
237 if (DriverName != NULL)
238 DriverName++;
239 else
240 DriverName = ObjectNameInfo->Name.Buffer;
241
242 DriverNameLength = wcslen (DriverName) * sizeof(WCHAR);
243 DPRINT ("Driver name '%S'\n", DriverName);
244 }
245 else
246 {
247 DriverName = NULL;
248 DriverNameLength = 0;
249 }
250
251 /* Allocate request buffer */
252 Request = ExAllocatePool (NonPagedPool,
253 sizeof(LPC_MAX_MESSAGE));
254 if (Request == NULL)
255 {
256 DPRINT ("Failed to allocate request buffer!\n");
257
258 /* Requeue log message and restart the worker */
259 ExInterlockedInsertTailList (&IopLogListHead,
260 &LogEntry->Entry,
261 &IopLogListLock);
262 IopRestartLogWorker ();
263
264 return;
265 }
266
267 /* Initialize the log message */
268 Message = (PIO_ERROR_LOG_MESSAGE)Request->Data;
269 Message->Type = 0xC; //IO_TYPE_ERROR_MESSAGE;
270 Message->Size =
271 sizeof(IO_ERROR_LOG_MESSAGE) - sizeof(IO_ERROR_LOG_PACKET) +
272 LogEntry->PacketSize + DriverNameLength;
273 Message->DriverNameLength = (USHORT)DriverNameLength;
274 Message->TimeStamp.QuadPart = LogEntry->TimeStamp.QuadPart;
275 Message->DriverNameOffset = (DriverName != NULL) ? LogEntry->PacketSize : 0;
276
277 /* Copy error log packet */
278 RtlCopyMemory (&Message->EntryData,
279 Packet,
280 LogEntry->PacketSize);
281
282 /* Copy driver or device name */
283 RtlCopyMemory ((PVOID)((ULONG_PTR)Message + Message->DriverNameOffset),
284 DriverName,
285 DriverNameLength);
286
287 DPRINT ("SequenceNumber %lx\n", Packet->SequenceNumber);
288
289 Request->Header.DataSize = Message->Size;
290 Request->Header.MessageSize =
291 Request->Header.DataSize + sizeof(LPC_MESSAGE);
292
293 /* Send the error message to the log port */
294 Status = NtRequestPort (IopLogPort,
295 &Request->Header);
296
297 /* Release request buffer */
298 ExFreePool (Request);
299
300 if (!NT_SUCCESS(Status))
301 {
302 DPRINT ("NtRequestPort() failed (Status %lx)\n", Status);
303
304 /* Requeue log message and restart the worker */
305 ExInterlockedInsertTailList (&IopLogListHead,
306 &LogEntry->Entry,
307 &IopLogListLock);
308 IopRestartLogWorker ();
309
310 return;
311 }
312
313 /* Release error log entry */
314 KeAcquireSpinLock (&IopAllocationLock,
315 &Irql);
316
317 IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY));
318 ExFreePool (LogEntry);
319
320 KeReleaseSpinLock (&IopAllocationLock,
321 Irql);
322 }
323
324 IopLogWorkerRunning = FALSE;
325
326 DPRINT ("IopLogWorker() done\n");
327 }
328
329
330 /*
331 * @implemented
332 */
333 PVOID STDCALL
334 IoAllocateErrorLogEntry (IN PVOID IoObject,
335 IN UCHAR EntrySize)
336 {
337 PERROR_LOG_ENTRY LogEntry;
338 ULONG LogEntrySize;
339 KIRQL Irql;
340
341 DPRINT("IoAllocateErrorLogEntry() called\n");
342
343 if (IoObject == NULL)
344 return NULL;
345
346 KeAcquireSpinLock (&IopAllocationLock,
347 &Irql);
348
349 if (IopTotalLogSize > PAGE_SIZE)
350 {
351 KeReleaseSpinLock (&IopAllocationLock,
352 Irql);
353 return NULL;
354 }
355
356 LogEntrySize = sizeof(ERROR_LOG_ENTRY) + EntrySize;
357 LogEntry = ExAllocatePool (NonPagedPool,
358 LogEntrySize);
359 if (LogEntry == NULL)
360 {
361 KeReleaseSpinLock (&IopAllocationLock,
362 Irql);
363 return NULL;
364 }
365
366 IopTotalLogSize += EntrySize;
367
368 LogEntry->IoObject = IoObject;
369 LogEntry->PacketSize = LogEntrySize;
370
371 KeReleaseSpinLock (&IopAllocationLock,
372 Irql);
373
374 return (PVOID)((ULONG_PTR)LogEntry + sizeof(ERROR_LOG_ENTRY));
375 }
376
377
378 /*
379 * @implemented
380 */
381 VOID STDCALL
382 IoFreeErrorLogEntry(IN PVOID ElEntry)
383 {
384 PERROR_LOG_ENTRY LogEntry;
385 KIRQL Irql;
386
387 DPRINT("IoFreeErrorLogEntry() called\n");
388
389 if (ElEntry == NULL)
390 return;
391
392 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
393
394 KeAcquireSpinLock(&IopAllocationLock,
395 &Irql);
396
397 IopTotalLogSize -= (LogEntry->PacketSize - sizeof(ERROR_LOG_ENTRY));
398 ExFreePool(LogEntry);
399
400 KeReleaseSpinLock(&IopAllocationLock,
401 Irql);
402 }
403
404
405 /*
406 * @implemented
407 */
408 VOID STDCALL
409 IoWriteErrorLogEntry (IN PVOID ElEntry)
410 {
411 PWORK_QUEUE_ITEM LogWorkItem;
412 PERROR_LOG_ENTRY LogEntry;
413 KIRQL Irql;
414
415 DPRINT("IoWriteErrorLogEntry() called\n");
416
417 LogEntry = (PERROR_LOG_ENTRY)((ULONG_PTR)ElEntry - sizeof(ERROR_LOG_ENTRY));
418
419 /* Get time stamp */
420 KeQuerySystemTime (&LogEntry->TimeStamp);
421
422 KeAcquireSpinLock (&IopLogListLock,
423 &Irql);
424
425 InsertHeadList (&IopLogListHead,
426 &LogEntry->Entry);
427
428 if (IopLogWorkerRunning == FALSE)
429 {
430 LogWorkItem = ExAllocatePool (NonPagedPool,
431 sizeof(WORK_QUEUE_ITEM));
432 if (LogWorkItem != NULL)
433 {
434 ExInitializeWorkItem (LogWorkItem,
435 IopLogWorker,
436 LogWorkItem);
437
438 ExQueueWorkItem (LogWorkItem,
439 DelayedWorkQueue);
440
441 IopLogWorkerRunning = TRUE;
442 }
443 }
444
445 KeReleaseSpinLock (&IopLogListLock,
446 Irql);
447
448 DPRINT("IoWriteErrorLogEntry() done\n");
449 }
450
451 /* EOF */