ReactOS 0.4.17-dev-769-g1500a35
transaction.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Arbitrartion Library
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Test/retest/commit/rollback transaction support
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8/* INCLUDES *******************************************************************/
9
10#include <ntifs.h>
11#include <ndk/rtlfuncs.h>
12#include "arbiter.h"
13
14#define NDEBUG
15#include <debug.h>
16
17/* TRANSACTION SUPPORT ********************************************************/
18
41CODE_SEG("PAGE")
42static
45 _In_ PARBITER_INSTANCE Arbiter,
47 _Out_ PARBITER_ALTERNATIVE Alternative)
48{
51
52 PAGED_CODE();
53
54 /*
55 * UnpackRequirement always writes 64-bit Length/Alignment; ARBITER_ALTERNATIVE
56 * narrows them to 32 bits pre-Vista, so decode through 64-bit temporaries and
57 * assign, just to keep both versions in source.
58 */
59 Alternative->Descriptor = Descriptor;
60 Status = Arbiter->UnpackRequirement(Descriptor,
61 &Alternative->Minimum,
62 &Alternative->Maximum,
63 &Length,
64 &Alignment);
65 if (!NT_SUCCESS(Status))
66 return Status;
67
68#if (NTDDI_VERSION >= NTDDI_VISTA)
69 Alternative->Length = Length;
70 Alternative->Alignment = Alignment;
71#else
72 Alternative->Length = (UINT32)Length;
73 Alternative->Alignment = (UINT32)Alignment;
74#endif
75
76 if (Alignment != 0 && (Alternative->Minimum % Alignment) != 0)
77 Alternative->Minimum += Alignment - (Alternative->Minimum % Alignment);
78
79 Alternative->Flags = 0;
80 Alternative->Priority = ARBITER_PRIORITY_NULL;
81
82 if (Descriptor->ShareDisposition == CmResourceShareShared)
83 Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_SHARED;
84
85 if (Alternative->Maximum < Alternative->Minimum)
86 Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_BADRANGE;
87 else if ((Alternative->Maximum - Alternative->Minimum + 1) == Alternative->Length)
88 Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_FIXED;
89
90 return STATUS_SUCCESS;
91}
92
116CODE_SEG("PAGE")
117static
120 _In_ PARBITER_INSTANCE Arbiter,
121 _In_ PLIST_ENTRY ArbitrationList,
122 _In_ ULONG EntryCount)
123{
125 PARBITER_ALTERNATIVE Alternative;
126 PLIST_ENTRY ListEntry;
127 ULONG StateCount = EntryCount + 1; /* + NULL terminator */
128 ULONG AlternativeCount = 0;
129 ULONG Size;
130
131 PAGED_CODE();
132
133 for (ListEntry = ArbitrationList->Flink;
134 ListEntry != ArbitrationList;
135 ListEntry = ListEntry->Flink)
136 {
138
139 if (Entry->AlternativeCount == 0)
140 StateCount--; /* an empty entry contributes no state */
141 else
142 AlternativeCount += Entry->AlternativeCount;
143 }
144
145 Size = StateCount * sizeof(ARBITER_ALLOCATION_STATE) +
146 AlternativeCount * sizeof(ARBITER_ALTERNATIVE);
147
148 if (Arbiter->AllocationStackMaxSize < Size)
149 {
151
153 if (NewStack == NULL)
155
156 if (Arbiter->AllocationStack != NULL)
157 ExFreePoolWithTag(Arbiter->AllocationStack, TAG_ARBITER);
158
159 Arbiter->AllocationStack = NewStack;
160 Arbiter->AllocationStackMaxSize = Size;
161 }
162 RtlZeroMemory(Arbiter->AllocationStack, Size);
163
164 State = Arbiter->AllocationStack;
165 Alternative = (PARBITER_ALTERNATIVE)&Arbiter->AllocationStack[StateCount];
166
167 for (ListEntry = ArbitrationList->Flink;
168 ListEntry != ArbitrationList;
169 ListEntry = ListEntry->Flink)
170 {
172 ULONG Index;
173
174 if (Entry->AlternativeCount == 0)
175 continue;
176
177 State->Entry = Entry;
178 State->AlternativeCount = Entry->AlternativeCount;
179 State->Alternatives = Alternative;
180 State->Start = 1; /* Start(1) > End(0): nothing chosen yet */
181
182 for (Index = 0; Index < Entry->AlternativeCount; ++Index)
183 {
184 NTSTATUS Status = ArbpBuildAlternative(Arbiter, &Entry->Alternatives[Index], Alternative);
185 if (!NT_SUCCESS(Status))
186 return Status;
187 Alternative++;
188 }
189 State++;
190 }
191
192 return STATUS_SUCCESS;
193}
194
217CODE_SEG("PAGE")
218static
221 _In_ PARBITER_INSTANCE Arbiter,
222 _In_ PLIST_ENTRY ArbitrationList)
223{
224 PLIST_ENTRY ListEntry;
225 PVOID PreviousOwner = NULL;
226 ULONG EntryCount = 0;
228
229 PAGED_CODE();
230
231 /* Start the tentative allocation as a copy of the committed one. */
232 RtlFreeRangeList(Arbiter->PossibleAllocation);
233 RtlInitializeRangeList(Arbiter->PossibleAllocation);
234 Status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
235 if (!NT_SUCCESS(Status))
236 goto Failure;
237
238 for (ListEntry = ArbitrationList->Flink;
239 ListEntry != ArbitrationList;
240 ListEntry = ListEntry->Flink)
241 {
243 ULONG Index;
244
245 EntryCount++;
246
247 /*
248 * Everything a device on the arbitration list already owns is up for
249 * reassignment - remove it from the working list so the device does not
250 * conflict with itself (its boot config in particular).
251 */
252 if (Entry->PhysicalDeviceObject != PreviousOwner)
253 {
254 PreviousOwner = Entry->PhysicalDeviceObject;
255 RtlDeleteOwnersRanges(Arbiter->PossibleAllocation, Entry->PhysicalDeviceObject);
256 }
257
258 /* Score each entry: the sum of its alternatives' constrainedness. */
259 Entry->WorkSpace = 0;
260 if (Arbiter->ScoreRequirement != NULL)
261 {
262 for (Index = 0; Index < Entry->AlternativeCount; ++Index)
263 {
264 INT32 Score = Arbiter->ScoreRequirement(&Entry->Alternatives[Index]);
265 if (Score < 0)
266 {
268 goto Failure;
269 }
270 Entry->WorkSpace += Score;
271 }
272 }
273 }
274
275 ArbiterLibSortArbitrationList(ArbitrationList);
276
277 Status = ArbpBuildAllocationStack(Arbiter, ArbitrationList, EntryCount);
278 if (!NT_SUCCESS(Status))
279 goto Failure;
280
281 Status = Arbiter->AllocateEntry(Arbiter, Arbiter->AllocationStack);
282 if (!NT_SUCCESS(Status))
283 goto Failure;
284
285 return STATUS_SUCCESS;
286
287Failure:
288 RtlFreeRangeList(Arbiter->PossibleAllocation);
289 RtlInitializeRangeList(Arbiter->PossibleAllocation);
290 return Status;
291}
292
320CODE_SEG("PAGE")
321static
324 _In_ PARBITER_INSTANCE Arbiter,
325 _In_ PLIST_ENTRY ArbitrationList)
326{
328 ARBITER_ALTERNATIVE Alternative;
329 PLIST_ENTRY ListEntry;
331
332 PAGED_CODE();
333
334 RtlZeroMemory(&State, sizeof(State));
335 RtlZeroMemory(&Alternative, sizeof(Alternative));
336 State.Alternatives = &Alternative;
337 State.CurrentAlternative = &Alternative;
338 State.AlternativeCount = 1;
339
340 /*
341 * Rebuild the tentative allocation from the committed one
342 * minus everything the listed devices already own.
343 */
344 RtlFreeRangeList(Arbiter->PossibleAllocation);
345 RtlInitializeRangeList(Arbiter->PossibleAllocation);
346 Status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
347 if (!NT_SUCCESS(Status))
348 goto Failure;
349
350 for (ListEntry = ArbitrationList->Flink;
351 ListEntry != ArbitrationList;
352 ListEntry = ListEntry->Flink)
353 {
355
356 Status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
357 Entry->PhysicalDeviceObject);
358 if (!NT_SUCCESS(Status))
359 goto Failure;
360 }
361
362 for (ListEntry = ArbitrationList->Flink;
363 ListEntry != ArbitrationList;
364 ListEntry = ListEntry->Flink)
365 {
368
369 if (Entry->Result == ArbiterResultNullRequest)
370 continue;
371
372 /* A retest without a preceding successful test is a caller bug. */
373 if (Entry->SelectedAlternative == NULL || Entry->Assignment == NULL)
374 {
376 goto Failure;
377 }
378
379 Status = ArbpBuildAlternative(Arbiter, Entry->SelectedAlternative, &Alternative);
380 if (!NT_SUCCESS(Status))
381 goto Failure;
382
383 State.Entry = Entry;
384 State.WorkSpace = 0;
385
386 Status = Arbiter->UnpackResource(Entry->Assignment, &State.Start, &Length);
387 if (!NT_SUCCESS(Status))
388 goto Failure;
389 State.End = State.Start + Length - 1;
390
391 Status = Arbiter->PreprocessEntry(Arbiter, &State);
392 if (!NT_SUCCESS(Status))
393 goto Failure;
394
395 if (Length != 0)
396 Arbiter->AddAllocation(Arbiter, &State);
397
399 {
401 State.Flags &= ~ARBITER_STATE_FLAG_WORKSPACE;
402 }
403 }
404
405 return STATUS_SUCCESS;
406
407Failure:
408 RtlFreeRangeList(Arbiter->PossibleAllocation);
409 RtlInitializeRangeList(Arbiter->PossibleAllocation);
410 return Status;
411}
412
429CODE_SEG("PAGE")
431NTAPI
432#if (NTDDI_VERSION >= NTDDI_VISTA)
434 _In_ PARBITER_INSTANCE Arbiter,
436{
437 PAGED_CODE();
438 return ArbpTestAllocation(Arbiter, Parameters->ArbitrationList);
439}
440#else
442 _In_ PARBITER_INSTANCE Arbiter,
443 _Inout_ PLIST_ENTRY ArbitrationList)
444{
445 PAGED_CODE();
446 return ArbpTestAllocation(Arbiter, ArbitrationList);
447}
448#endif
449
465CODE_SEG("PAGE")
467NTAPI
468#if (NTDDI_VERSION >= NTDDI_VISTA)
470 _In_ PARBITER_INSTANCE Arbiter,
472{
473 PAGED_CODE();
474 return ArbpRetestAllocation(Arbiter, Parameters->ArbitrationList);
475}
476#else
478 _In_ PARBITER_INSTANCE Arbiter,
479 _Inout_ PLIST_ENTRY ArbitrationList)
480{
481 PAGED_CODE();
482 return ArbpRetestAllocation(Arbiter, ArbitrationList);
483}
484#endif
485
498CODE_SEG("PAGE")
500NTAPI
502 _In_ PARBITER_INSTANCE Arbiter)
503{
504 PRTL_RANGE_LIST Old = Arbiter->Allocation;
505
506 PAGED_CODE();
507
508 RtlFreeRangeList(Old);
510 Arbiter->Allocation = Arbiter->PossibleAllocation;
511 Arbiter->PossibleAllocation = Old;
512 return STATUS_SUCCESS;
513}
514
526CODE_SEG("PAGE")
528NTAPI
530 _In_ PARBITER_INSTANCE Arbiter)
531{
532 PAGED_CODE();
533
534 RtlFreeRangeList(Arbiter->PossibleAllocation);
535 RtlInitializeRangeList(Arbiter->PossibleAllocation);
536 return STATUS_SUCCESS;
537}
#define PAGED_CODE()
#define CODE_SEG(...)
COMPILER_DEPENDENT_UINT64 UINT64
Definition: actypes.h:131
NTSTATUS NTAPI ArbiterLibSortArbitrationList(_Inout_ PLIST_ENTRY ArbitrationList)
Definition: arbiter.c:139
#define ARBITER_ALTERNATIVE_FLAG_BADRANGE
Definition: arbiter.h:30
#define ARBITER_ALTERNATIVE_FLAG_SHARED
Definition: arbiter.h:29
struct _ARBITER_ALTERNATIVE * PARBITER_ALTERNATIVE
struct _ARBITER_ALLOCATION_STATE ARBITER_ALLOCATION_STATE
#define ARBITER_PRIORITY_NULL
Definition: arbiter.h:22
#define TAG_ARBITER
Definition: arbiter.h:10
#define ARBITER_STATE_FLAG_WORKSPACE
Definition: arbiter.h:52
#define ARBITER_ALTERNATIVE_FLAG_FIXED
Definition: arbiter.h:28
LONG NTSTATUS
Definition: precomp.h:26
_In_ HANDLE _In_ CONST PDXGKMDT_OPM_GET_INFO_PARAMETERS Parameters
Definition: dispmprt.h:321
#define NULL
Definition: types.h:112
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define PagedPool
Definition: env_spec_w32.h:308
union Alignment_ Alignment
Status
Definition: gdiplustypes.h:24
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
NTSYSAPI NTSTATUS NTAPI RtlDeleteOwnersRanges(_Inout_ PRTL_RANGE_LIST RangeList, _In_ _Maybenull_ PVOID Owner)
NTSYSAPI VOID NTAPI RtlInitializeRangeList(_Out_ PRTL_RANGE_LIST RangeList)
NTSYSAPI NTSTATUS NTAPI RtlCopyRangeList(_Out_ PRTL_RANGE_LIST CopyRangeList, _In_ PRTL_RANGE_LIST RangeList)
NTSYSAPI VOID NTAPI RtlFreeRangeList(_In_ PRTL_RANGE_LIST RangeList)
#define _Inout_
Definition: no_sal2.h:162
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
#define STATUS_DEVICE_CONFIGURATION_ERROR
Definition: ntstatus.h:713
for(i=0;i< sizeof(testsuite)/sizeof(testsuite[0]);++i) ok(call_test(testsuite[i].func)
Entry
Definition: section.c:5216
#define STATUS_SUCCESS
Definition: shellext.h:65
Definition: iotypes.h:4633
Definition: typedefs.h:120
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
NTSTATUS NTAPI ArbiterLibRetestAllocation(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_RETEST_ALLOCATION_PARAMETERS Parameters)
The RetestAllocation action: deterministically re-establishes the placements a previous test chose,...
Definition: transaction.c:469
NTSTATUS NTAPI ArbiterLibTestAllocation(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_TEST_ALLOCATION_PARAMETERS Parameters)
The TestAllocation action: tentatively places every entry of the arbitration list,...
Definition: transaction.c:433
static NTSTATUS ArbpBuildAllocationStack(_In_ PARBITER_INSTANCE Arbiter, _In_ PLIST_ENTRY ArbitrationList, _In_ ULONG EntryCount)
Builds the allocation stack for an arbitration list into Arbiter->AllocationStack: one ARBITER_ALLOCA...
Definition: transaction.c:119
NTSTATUS NTAPI ArbiterLibCommitAllocation(_In_ PARBITER_INSTANCE Arbiter)
The CommitAllocation action: the tentative PossibleAllocation becomes the committed Allocation,...
Definition: transaction.c:501
NTSTATUS NTAPI ArbiterLibRollbackAllocation(_In_ PARBITER_INSTANCE Arbiter)
The RollbackAllocation action: discards the tentative allocation; the committed one is untouched.
Definition: transaction.c:529
static NTSTATUS ArbpTestAllocation(_In_ PARBITER_INSTANCE Arbiter, _In_ PLIST_ENTRY ArbitrationList)
Test-allocates every entry on an arbitration list into the arbiter's tentative allocation....
Definition: transaction.c:220
static NTSTATUS ArbpBuildAlternative(_In_ PARBITER_INSTANCE Arbiter, _In_ PIO_RESOURCE_DESCRIPTOR Descriptor, _Out_ PARBITER_ALTERNATIVE Alternative)
Expands one IO_RESOURCE_DESCRIPTOR into an ARBITER_ALTERNATIVE through the arbiter's UnpackRequiremen...
Definition: transaction.c:44
static NTSTATUS ArbpRetestAllocation(_In_ PARBITER_INSTANCE Arbiter, _In_ PLIST_ENTRY ArbitrationList)
Re-establishes a previously tested solution in the arbiter's tentative allocation without searching a...
Definition: transaction.c:323
int32_t INT32
Definition: typedefs.h:58
#define NTAPI
Definition: typedefs.h:36
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
uint32_t UINT32
Definition: typedefs.h:59
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
_In_ WDFCOLLECTION _In_ ULONG Index
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539
_Must_inspect_result_ _In_ WDFIORESLIST _In_ PIO_RESOURCE_DESCRIPTOR Descriptor
Definition: wdfresource.h:342
@ CmResourceShareShared
Definition: cmtypes.h:243
@ ArbiterResultNullRequest
Definition: iotypes.h:4628