1 /*++
2
3 Copyright (c) Microsoft Corporation. All rights reserved.
4
5 Module Name:
6
7 FxTransactionedList.cpp
8
9 Abstract:
10
11 This module implements a simple transactioned list which allows the caller
12 to lock the list and then iterate over it without worrying about deletes
13 and adds.
14
15 Author:
16
17
18
19 Environment:
20
21 Both kernel and user mode
22
23 Revision History:
24
25 --*/
26
27 #include "fxsupportpch.hpp"
28
FxTransactionedList()29 FxTransactionedList::FxTransactionedList()
30 {
31 m_ListLockedRecursionCount = 0;
32 m_DeleteOnRemove = FALSE;
33 m_Deleting = FALSE;
34 m_Retries = 0;
35 m_DeletingDoneEvent = NULL;
36
37 InitializeListHead(&m_ListHead);
38 InitializeListHead(&m_TransactionHead);
39 }
40
~FxTransactionedList()41 FxTransactionedList::~FxTransactionedList()
42 {
43 FxTransactionedEntry* pEntry;
44 PLIST_ENTRY ple;
45
46 //
47 // If m_DeleteOnRemove is FALSE, there is no need to iterate over any of the
48 // lists to free anything.
49 //
50 if (m_DeleteOnRemove == FALSE) {
51 ASSERT(IsListEmpty(&m_ListHead));
52 ASSERT(IsListEmpty(&m_TransactionHead));
53 return;
54 }
55
56 ASSERT(m_ListLockedRecursionCount == 0);
57
58 while (!IsListEmpty(&m_ListHead)) {
59 ple = RemoveHeadList(&m_ListHead);
60 InitializeListHead(ple);
61
62 pEntry = FxTransactionedEntry::_FromEntry(ple);
63
64 switch (pEntry->m_Transaction) {
65 case FxTransactionActionNothing:
66 //
67 // Nothing to do, no pending transaction
68 //
69 break;
70
71 case FxTransactionActionAdd:
72 //
73 // Should not have an add transaction and be on the main list at the
74 // same time!
75 //
76 ASSERT(FALSE);
77 break;
78
79 case FxTransactionActionRemove:
80 //
81 // Make sure it is not on the transaction list
82 //
83 RemoveEntryList(&pEntry->m_TransactionLink);
84 InitializeListHead(&pEntry->m_TransactionLink);
85
86 //
87 // When inserted as a remove transaction, we add this reference in
88 // RemoveLocked
89 //
90 pEntry->GetTransactionedObject()->RELEASE(pEntry);
91 break;
92
93 }
94
95 pEntry->GetTransactionedObject()->DeleteObject();
96 }
97
98 while (!IsListEmpty(&m_TransactionHead)) {
99 ple = RemoveHeadList(&m_TransactionHead);
100 InitializeListHead(ple);
101
102 pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
103
104 //
105 // We yanked out all of the removes in the previous loop
106 //
107 ASSERT(pEntry->m_Transaction == FxTransactionActionAdd);
108
109 //
110 // Delete the object since this list owns it.
111 //
112 pEntry->GetTransactionedObject()->DeleteObject();
113 }
114 }
115
116 VOID
LockForEnum(__in PFX_DRIVER_GLOBALS FxDriverGlobals)117 FxTransactionedList::LockForEnum(
118 __in PFX_DRIVER_GLOBALS FxDriverGlobals
119 )
120 {
121 KIRQL irql;
122
123 AcquireLock(FxDriverGlobals, &irql);
124 m_ListLockedRecursionCount++;
125 ReleaseLock(FxDriverGlobals, irql);
126 }
127
128 VOID
UnlockFromEnum(__in PFX_DRIVER_GLOBALS FxDriverGlobals)129 FxTransactionedList::UnlockFromEnum(
130 __in PFX_DRIVER_GLOBALS FxDriverGlobals
131 )
132 {
133 LIST_ENTRY releaseHead;
134 KIRQL irql;
135 MxEvent* event;
136
137 InitializeListHead(&releaseHead);
138 event = NULL;
139 AcquireLock(FxDriverGlobals, &irql);
140 m_ListLockedRecursionCount--;
141 ProcessTransactionList(&releaseHead);
142
143 if (m_ListLockedRecursionCount == 0 && m_Deleting) {
144 event = m_DeletingDoneEvent;
145 m_DeletingDoneEvent = NULL;
146 }
147 ReleaseLock(FxDriverGlobals, irql);
148
149 ProcessObjectsToRelease(&releaseHead);
150
151 if (event != NULL) {
152 event->Set();
153 }
154 }
155
156 VOID
ProcessTransactionList(__in PLIST_ENTRY ReleaseHead)157 FxTransactionedList::ProcessTransactionList(
158 __in PLIST_ENTRY ReleaseHead
159 )
160 {
161 LIST_ENTRY *ple;
162 FxTransactionedEntry* pEntry;
163
164 //
165 // If there are other iterators, do not process transactions until they are
166 // done.
167 //
168 if (m_ListLockedRecursionCount != 0) {
169 return;
170 }
171
172 while (!IsListEmpty(&m_TransactionHead)) {
173 ple = RemoveHeadList(&m_TransactionHead);
174 InitializeListHead(ple);
175
176 pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
177
178 ASSERT(pEntry->m_Transaction != FxTransactionActionNothing);
179
180 if (pEntry->m_Transaction == FxTransactionActionAdd) {
181 //
182 // Add to the main list
183 //
184 InsertTailList(&m_ListHead, &pEntry->m_ListLink);
185
186 //
187 // Virtual notification of addition
188 //
189 EntryAdded(pEntry);
190 }
191 else if (pEntry->m_Transaction == FxTransactionActionRemove) {
192 //
193 // Remove it from the main list and move it to a free list
194 //
195 RemoveEntryList(&pEntry->m_ListLink);
196 InsertTailList(ReleaseHead, &pEntry->m_TransactionLink);
197
198 //
199 // Virtual notification of removal
200 //
201 EntryRemoved(pEntry);
202 }
203
204 pEntry->m_Transaction = FxTransactionActionNothing;
205 }
206 }
207
208 VOID
ProcessObjectsToRelease(__in PLIST_ENTRY ReleaseHead)209 FxTransactionedList::ProcessObjectsToRelease(
210 __in PLIST_ENTRY ReleaseHead
211 )
212 {
213 LIST_ENTRY *ple;
214 FxTransactionedEntry* pEntry;
215
216 while (!IsListEmpty(ReleaseHead)) {
217 ple = RemoveHeadList(ReleaseHead);
218 InitializeListHead(ple);
219
220 pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
221
222 //
223 // We always release our reference we took when we post the change
224 // to the list
225 //
226 pEntry->GetTransactionedObject()->RELEASE(pEntry);
227
228 //
229 // 2ndary release if the list is set to do this
230 //
231 if (m_DeleteOnRemove) {
232 pEntry->GetTransactionedObject()->DeleteObject();
233 }
234 }
235 }
236
237 BOOLEAN
Deleting(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in_opt MxEvent * DeleteDoneEvent)238 FxTransactionedList::Deleting(
239 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
240 __in_opt MxEvent* DeleteDoneEvent
241 )
242 {
243 KIRQL irql;
244 BOOLEAN result;
245
246 result = TRUE;
247
248 AcquireLock(FxDriverGlobals, &irql);
249 m_Deleting = TRUE;
250
251 if (m_ListLockedRecursionCount != 0) {
252 m_DeletingDoneEvent = DeleteDoneEvent;
253 result = FALSE;
254 }
255
256 ReleaseLock(FxDriverGlobals, irql);
257
258 return result;
259 }
260
261 _Must_inspect_result_
262 NTSTATUS
Add(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxTransactionedEntry * Entry)263 FxTransactionedList::Add(
264 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
265 __in FxTransactionedEntry* Entry
266 )
267 {
268 NTSTATUS status;
269 KIRQL irql;
270
271 AcquireLock(FxDriverGlobals, &irql);
272
273 if (m_Deleting) {
274 status = STATUS_INVALID_DEVICE_STATE;
275 }
276 else {
277 status = ProcessAdd(Entry);
278 }
279
280 if (NT_SUCCESS(status)) {
281 if (m_ListLockedRecursionCount == 0) {
282 //
283 // We can insert the entry now, do so
284 //
285 InsertTailList(&m_ListHead, &Entry->m_ListLink);
286
287 EntryAdded(Entry);
288 }
289 else {
290 //
291 // List is locked, queue a transaction
292 //
293 Entry->m_Transaction = FxTransactionActionAdd;
294 InsertTailList(&m_TransactionHead, &Entry->m_TransactionLink);
295 }
296 }
297
298 ReleaseLock(FxDriverGlobals, irql);
299
300 return status;
301 }
302
303 VOID
SearchForAndRemove(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PVOID EntryData)304 FxTransactionedList::SearchForAndRemove(
305 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
306 __in PVOID EntryData
307 )
308 {
309 KIRQL irql;
310 FxTransactionedEntry* pEntry;
311 PLIST_ENTRY ple;
312 BOOLEAN removed;
313
314 removed = FALSE;
315
316 AcquireLock(FxDriverGlobals, &irql);
317
318 for (ple = m_TransactionHead.Flink;
319 ple != &m_TransactionHead;
320 ple = ple->Flink) {
321
322 pEntry = CONTAINING_RECORD(ple, FxTransactionedEntry, m_TransactionLink);
323
324 if (Compare(pEntry, EntryData)) {
325 if (pEntry->GetTransactionAction() == FxTransactionActionAdd) {
326 RemoveEntryList(&pEntry->m_TransactionLink);
327 InitializeListHead(&pEntry->m_TransactionLink);
328
329 removed = TRUE;
330 }
331 else {
332 //
333 // Already being removed, just return
334 //
335 ASSERT(pEntry->GetTransactionAction() ==
336 FxTransactionActionRemove);
337 }
338
339 goto Done;
340 }
341 }
342
343 //
344 // Walk the committed list
345 //
346 pEntry = NULL;
347
348 while ((pEntry = GetNextEntryLocked(pEntry)) != NULL) {
349 if (Compare(pEntry, EntryData)) {
350 removed = RemoveLocked(pEntry);
351 break;
352 }
353 }
354
355 Done:
356 ReleaseLock(FxDriverGlobals, irql);
357
358 if (removed && m_DeleteOnRemove) {
359 pEntry->GetTransactionedObject()->DeleteObject();
360 }
361 }
362
363 VOID
Remove(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxTransactionedEntry * Entry)364 FxTransactionedList::Remove(
365 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
366 __in FxTransactionedEntry* Entry
367 )
368 {
369 BOOLEAN removed;
370 KIRQL irql;
371
372 AcquireLock(FxDriverGlobals, &irql);
373 removed = RemoveLocked(Entry);
374 ReleaseLock(FxDriverGlobals,irql);
375
376 if (removed && m_DeleteOnRemove) {
377 Entry->GetTransactionedObject()->DeleteObject();
378 }
379 }
380
381 BOOLEAN
RemoveLocked(__in FxTransactionedEntry * Entry)382 FxTransactionedList::RemoveLocked(
383 __in FxTransactionedEntry* Entry
384 )
385 {
386 BOOLEAN removed;
387
388 removed = FALSE;
389
390 if (Entry->m_Transaction == FxTransactionActionAdd) {
391 //
392 // Not yet added to the list proper, remove it from the transaction list
393 //
394 removed = TRUE;
395 RemoveEntryList(&Entry->m_TransactionLink);
396 InitializeListHead(&Entry->m_TransactionLink);
397
398 Entry->m_Transaction = FxTransactionActionNothing;
399 }
400 else {
401 ASSERT(!IsListEmpty(&Entry->m_ListLink));
402
403 if (m_ListLockedRecursionCount == 0) {
404 //
405 // List is not locked, remove it now
406 //
407 RemoveEntryList(&Entry->m_ListLink);
408 InitializeListHead(&Entry->m_ListLink);
409
410 //
411 // Virtual notification
412 //
413 EntryRemoved(Entry);
414
415 removed = TRUE;
416 }
417 else {
418 //
419 // List is locked for enumeration, queue a transaction
420 //
421 Entry->m_Transaction = FxTransactionActionRemove;
422 InsertTailList(&m_TransactionHead, &Entry->m_TransactionLink);
423 Entry->GetTransactionedObject()->ADDREF(Entry);
424 }
425 }
426
427 return removed;
428 }
429
430 _Must_inspect_result_
431 FxTransactionedEntry*
GetNextEntry(__in_opt FxTransactionedEntry * Entry)432 FxTransactionedList::GetNextEntry(
433 __in_opt FxTransactionedEntry* Entry
434 )
435 /*++
436
437 Routine Description:
438 Gets the next entry. Assumes the caller has called LockedForEnum
439
440 Arguments:
441 Entry the current entry in the iteratation, NULL for the first
442
443 Return Value:
444 next entry in the iteration, NULL if there are no more entries
445
446 --*/
447 {
448 //
449 // The caller should have locked the list for enumeration
450 //
451 ASSERT(m_ListLockedRecursionCount > 0 || m_Deleting);
452
453 return GetNextEntryLocked(Entry);
454 }
455
456 _Must_inspect_result_
457 FxTransactionedEntry*
GetNextEntryLocked(__in_opt FxTransactionedEntry * Entry)458 FxTransactionedList::GetNextEntryLocked(
459 __in_opt FxTransactionedEntry* Entry
460 )
461 /*++
462
463 Routine Description:
464 Returns the next entry. Assumes that the caller has the list locked through
465 a call to AcquireLock() or through LockForEnum()
466
467 Arguments:
468 Entry the current entry in the iteratation, NULL for the first
469
470 Return Value:
471 next entry in the iteration, NULL if there are no more entries
472
473 --*/
474 {
475 PLIST_ENTRY ple;
476
477 if (Entry == NULL) {
478 ple = m_ListHead.Flink;
479 }
480 else {
481 ple = Entry->m_ListLink.Flink;
482 }
483
484 //
485 // Find the next entry which does not have a pending transaction on it
486 //
487 for ( ; ple != &m_ListHead; ple = ple->Flink) {
488 FxTransactionedEntry* pNext;
489
490 pNext = FxTransactionedEntry::_FromEntry(ple);
491 if (pNext->m_Transaction == FxTransactionActionNothing) {
492 return pNext;
493 }
494 }
495
496 //
497 // Reached the end of the list
498 //
499 return NULL;
500 }
501
FxSpinLockTransactionedList()502 FxSpinLockTransactionedList::FxSpinLockTransactionedList() :
503 FxTransactionedList()
504 {
505 }
506
507 __drv_raisesIRQL(DISPATCH_LEVEL)
__drv_maxIRQL(DISPATCH_LEVEL)508 __drv_maxIRQL(DISPATCH_LEVEL)
509 VOID
510 FxSpinLockTransactionedList::AcquireLock(
511 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
512 __out PKIRQL Irql
513 )
514 {
515 UNREFERENCED_PARAMETER(FxDriverGlobals);
516 m_ListLock.Acquire(Irql);
517 }
518
__drv_requiresIRQL(DISPATCH_LEVEL)519 __drv_requiresIRQL(DISPATCH_LEVEL)
520 VOID
521 FxSpinLockTransactionedList::ReleaseLock(
522 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
523 __in __drv_restoresIRQL KIRQL Irql
524 )
525 {
526 UNREFERENCED_PARAMETER(FxDriverGlobals);
527 m_ListLock.Release(Irql);
528 }
529
_Acquires_lock_(_Global_critical_region_)530 _Acquires_lock_(_Global_critical_region_)
531 VOID
532 FxWaitLockTransactionedList::AcquireLock(
533 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
534 __out PKIRQL Irql
535 )
536 {
537 UNREFERENCED_PARAMETER(Irql);
538 m_StateChangeListLock.AcquireLock(FxDriverGlobals);
539 }
540
_Releases_lock_(_Global_critical_region_)541 _Releases_lock_(_Global_critical_region_)
542 VOID
543 FxWaitLockTransactionedList::ReleaseLock(
544 __in PFX_DRIVER_GLOBALS FxDriverGlobals,
545 __in KIRQL Irql
546 )
547 {
548 UNREFERENCED_PARAMETER(Irql);
549 m_StateChangeListLock.ReleaseLock(FxDriverGlobals);
550 }
551
552
553