From: Paul Durrant Date: Fri, 11 Apr 2014 10:09:20 +0000 (+0100) Subject: Re-worked GNTTAB. X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=117d0e93d39851392a6fa351b01ed96daaf3d4dc;p=people%2Fpauldu%2Fxenbus.git Re-worked GNTTAB. The new version of the interface no longer exposes raw references to clients. They are now behind an opaque XENVIF_GNTTAB_DESCRIPTOR, analogous to the XENVIF_EVTCHN_DESCRIPTOR which hides raw ports from clients. The allocator is also more sophisticated. It uses a pool with per-cpu magazines for lockless access backed by rangesets. Signed-off-by: Paul Durrant --- diff --git a/include/gnttab_interface.h b/include/gnttab_interface.h index 65e62e0..6b57ce8 100644 --- a/include/gnttab_interface.h +++ b/include/gnttab_interface.h @@ -37,66 +37,56 @@ typedef enum _XENBUS_GNTTAB_ENTRY_TYPE { GNTTAB_ENTRY_FULL_PAGE } XENBUS_GNTTAB_ENTRY_TYPE, *PXENBUS_GNTTAB_ENTRY_TYPE; -typedef struct _XENBUS_GNTTAB_COPY_OPERATION { - LIST_ENTRY ListEntry; - USHORT RemoteDomain; - ULONG RemoteReference; - ULONG RemoteOffset; - PFN_NUMBER Pfn; - ULONG Offset; - ULONG Length; -} XENBUS_GNTTAB_COPY_OPERATION, *PXENBUS_GNTTAB_COPY_OPERATION; +typedef struct _XENBUS_GNTTAB_DESCRIPTOR XENBUS_GNTTAB_DESCRIPTOR, *PXENBUS_GNTTAB_DESCRIPTOR; #define DEFINE_GNTTAB_OPERATIONS \ GNTTAB_OPERATION(VOID, \ Acquire, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context \ + IN PXENBUS_GNTTAB_CONTEXT Context \ ) \ ) \ GNTTAB_OPERATION(VOID, \ Release, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context \ + IN PXENBUS_GNTTAB_CONTEXT Context \ ) \ ) \ - GNTTAB_OPERATION(NTSTATUS, \ + GNTTAB_OPERATION(PXENBUS_GNTTAB_DESCRIPTOR, \ Get, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context, \ - OUT PULONG Reference \ + IN PXENBUS_GNTTAB_CONTEXT Context \ ) \ ) \ GNTTAB_OPERATION(VOID, \ Put, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context, \ - IN ULONG Reference \ + IN PXENBUS_GNTTAB_CONTEXT Context, \ + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor \ ) \ ) \ GNTTAB_OPERATION(NTSTATUS, \ PermitForeignAccess, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context, \ - IN ULONG Reference, \ - IN USHORT Domain, \ - IN XENBUS_GNTTAB_ENTRY_TYPE Type, \ + IN PXENBUS_GNTTAB_CONTEXT Context, \ + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor, \ + IN USHORT Domain, \ + IN XENBUS_GNTTAB_ENTRY_TYPE Type, \ ... \ ) \ ) \ GNTTAB_OPERATION(NTSTATUS, \ RevokeForeignAccess, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context, \ - IN ULONG Reference \ + IN PXENBUS_GNTTAB_CONTEXT Context, \ + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor \ ) \ ) \ - GNTTAB_OPERATION(NTSTATUS, \ - Copy, \ + GNTTAB_OPERATION(ULONG, \ + Reference, \ ( \ - IN PXENBUS_GNTTAB_CONTEXT Context, \ - IN PLIST_ENTRY List, \ - IN ULONG Count \ + IN PXENBUS_GNTTAB_CONTEXT Context, \ + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor \ ) \ ) @@ -127,7 +117,7 @@ DEFINE_GUID(GUID_GNTTAB_INTERFACE, 0xd6, 0xe); -#define GNTTAB_INTERFACE_VERSION 3 +#define GNTTAB_INTERFACE_VERSION 4 #define GNTTAB_OPERATIONS(_Interface) \ (PXENBUS_GNTTAB_OPERATIONS *)((ULONG_PTR)(_Interface)) diff --git a/proj/xenbus/xenbus.vcxproj b/proj/xenbus/xenbus.vcxproj index 779d3a2..f4085be 100644 --- a/proj/xenbus/xenbus.vcxproj +++ b/proj/xenbus/xenbus.vcxproj @@ -90,6 +90,7 @@ + diff --git a/src/xenbus/gnttab.c b/src/xenbus/gnttab.c index 8ae786f..1fcf236 100644 --- a/src/xenbus/gnttab.c +++ b/src/xenbus/gnttab.c @@ -37,27 +37,22 @@ #include "gnttab.h" #include "fdo.h" +#include "range_set.h" +#include "pool.h" #include "dbg_print.h" #include "assert.h" -#define GNTTAB_MAXIMUM_FRAME_COUNT 32 -#define GNTTAB_ENTRY_PER_FRAME (PAGE_SIZE / sizeof (grant_entry_v1_t)) +#define GNTTAB_MAXIMUM_FRAME_COUNT 32 +#define GNTTAB_ENTRY_PER_FRAME (PAGE_SIZE / sizeof (grant_entry_v1_t)) // Xen requires that we avoid the first 8 entries of the table and // we also reserve 1 entry for the crash kernel #define GNTTAB_RESERVED_ENTRY_COUNT 9 -#define GNTTAB_INVALID_REFERENCE 0 - -#define GNTTAB_IS_INVALID_REFERENCE(_Reference) \ - ((_Reference) < GNTTAB_RESERVED_ENTRY_COUNT) - -typedef struct _GNTTAB_DESCRIPTOR { - LIST_ENTRY ListEntry; - ULONG Next; - PVOID Caller; +struct _XENBUS_GNTTAB_DESCRIPTOR { + ULONG Reference; grant_entry_v1_t Entry; -} GNTTAB_DESCRIPTOR, *PGNTTAB_DESCRIPTOR; +}; struct _XENBUS_GNTTAB_CONTEXT { LONG References; @@ -65,10 +60,8 @@ struct _XENBUS_GNTTAB_CONTEXT { ULONG FrameCount; grant_entry_v1_t *Entry; KSPIN_LOCK Lock; - ULONG HeadFreeReference; - PULONG TailFreeReference; - GNTTAB_DESCRIPTOR Descriptor[GNTTAB_MAXIMUM_FRAME_COUNT * GNTTAB_ENTRY_PER_FRAME]; - LIST_ENTRY List; + PXENBUS_RANGE_SET RangeSet; + PXENBUS_POOL DescriptorPool; PXENBUS_SUSPEND_INTERFACE SuspendInterface; PXENBUS_SUSPEND_CALLBACK SuspendCallbackEarly; PXENBUS_DEBUG_INTERFACE DebugInterface; @@ -93,16 +86,15 @@ __GnttabFree( __FreePoolWithTag(Buffer, GNTTAB_TAG); } -static NTSTATUS -GnttabExpand( +static FORCEINLINE NTSTATUS +__GnttabExpand( IN PXENBUS_GNTTAB_CONTEXT Context ) { ULONG FrameIndex; PFN_NUMBER Pfn; - ULONG Start; - ULONG End; - ULONG Reference; + ULONGLONG Start; + ULONGLONG End; NTSTATUS status; FrameIndex = Context->FrameCount; @@ -124,16 +116,9 @@ GnttabExpand( Start = __max(GNTTAB_RESERVED_ENTRY_COUNT, FrameIndex * GNTTAB_ENTRY_PER_FRAME); End = (Context->FrameCount * GNTTAB_ENTRY_PER_FRAME) - 1; - Trace("adding refrences [%08x - %08x]\n", Start, End); + Trace("adding refrences [%08llx - %08llx]\n", Start, End); - for (Reference = Start; Reference <= End; Reference++) { - PGNTTAB_DESCRIPTOR Descriptor = &Context->Descriptor[Reference]; - - *Context->TailFreeReference = Reference; - - ASSERT(GNTTAB_IS_INVALID_REFERENCE(Descriptor->Next)); - Context->TailFreeReference = &Descriptor->Next; - } + RangeSetPut(Context->RangeSet, Start, End); return STATUS_SUCCESS; @@ -143,133 +128,180 @@ fail1: return status; } -static FORCEINLINE VOID -__GnttabEmpty( - IN PXENBUS_GNTTAB_CONTEXT Context +static NTSTATUS +GnttabDescriptorCtor( + IN PVOID Argument, + IN PVOID Object ) { - while (!GNTTAB_IS_INVALID_REFERENCE(Context->HeadFreeReference)) { - ULONG Reference = Context->HeadFreeReference; - PGNTTAB_DESCRIPTOR Descriptor = &Context->Descriptor[Reference]; + PXENBUS_GNTTAB_CONTEXT Context = Argument; + PXENBUS_GNTTAB_DESCRIPTOR Descriptor = Object; + NTSTATUS status; - Context->HeadFreeReference = Descriptor->Next; - Descriptor->Next = GNTTAB_INVALID_REFERENCE; - } + if (!RangeSetIsEmpty(Context->RangeSet)) + goto done; - Context->TailFreeReference = &Context->HeadFreeReference; -} + status = __GnttabExpand(Context); + if (!NT_SUCCESS(status)) + goto fail1; -extern USHORT -RtlCaptureStackBackTrace( - __in ULONG FramesToSkip, - __in ULONG FramesToCapture, - __out PVOID *BackTrace, - __out_opt PULONG BackTraceHash - ); +done: + Descriptor->Reference = (ULONG)RangeSetPop(Context->RangeSet); -static NTSTATUS -GnttabGet( - IN PXENBUS_GNTTAB_CONTEXT Context, - OUT PULONG Reference + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +static VOID +GnttabDescriptorDtor( + IN PVOID Argument, + IN PVOID Object ) { - PGNTTAB_DESCRIPTOR Descriptor; - KIRQL Irql; + PXENBUS_GNTTAB_CONTEXT Context = Argument; + PXENBUS_GNTTAB_DESCRIPTOR Descriptor = Object; NTSTATUS status; - KeAcquireSpinLock(&Context->Lock, &Irql); - - if (!GNTTAB_IS_INVALID_REFERENCE(Context->HeadFreeReference)) - goto done; + status = RangeSetPut(Context->RangeSet, + (ULONGLONG)Descriptor->Reference, + (ULONGLONG)Descriptor->Reference); + ASSERT(NT_SUCCESS(status)); +} - ASSERT3P(Context->TailFreeReference, ==, &Context->HeadFreeReference); +static FORCEINLINE VOID +__drv_requiresIRQL(DISPATCH_LEVEL) +__GnttabAcquireLock( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL); - status = GnttabExpand(Context); - if (!NT_SUCCESS(status)) - goto fail1; + KeAcquireSpinLockAtDpcLevel(&Context->Lock); +} -done: - ASSERT(!GNTTAB_IS_INVALID_REFERENCE(Context->HeadFreeReference)); +static DECLSPEC_NOINLINE VOID +GnttabAcquireLock( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + __GnttabAcquireLock(Context); +} - *Reference = Context->HeadFreeReference; - Descriptor = &Context->Descriptor[*Reference]; +static FORCEINLINE VOID +__drv_requiresIRQL(DISPATCH_LEVEL) +__GnttabReleaseLock( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL); - Context->HeadFreeReference = Descriptor->Next; +#pragma prefast(disable:26110) + KeReleaseSpinLockFromDpcLevel(&Context->Lock); +} - if (GNTTAB_IS_INVALID_REFERENCE(Context->HeadFreeReference)) - Context->TailFreeReference = &Context->HeadFreeReference; +static DECLSPEC_NOINLINE VOID +GnttabReleaseLock( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + __GnttabReleaseLock(Context); +} - Descriptor->Next = GNTTAB_INVALID_REFERENCE; +static FORCEINLINE NTSTATUS +__GnttabFill( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + NTSTATUS status; - ASSERT(IsZeroMemory(Descriptor, sizeof (GNTTAB_DESCRIPTOR))); + status = RangeSetInitialize(&Context->RangeSet); + if (!NT_SUCCESS(status)) + goto fail1; - InsertTailList(&Context->List, &Descriptor->ListEntry); + status = PoolInitialize("GnttabDescriptor", + sizeof (XENBUS_GNTTAB_DESCRIPTOR), + GnttabDescriptorCtor, + GnttabDescriptorDtor, + GnttabAcquireLock, + GnttabReleaseLock, + Context, + &Context->DescriptorPool); + if (!NT_SUCCESS(status)) + goto fail2; - KeReleaseSpinLock(&Context->Lock, Irql); + return STATUS_SUCCESS; - (VOID) RtlCaptureStackBackTrace(1, 1, &Descriptor->Caller, NULL); +fail2: + Error("fail2\n"); - return STATUS_SUCCESS; + RangeSetTeardown(Context->RangeSet); + Context->RangeSet = NULL; fail1: Error("fail1 (%08x)\n", status); - KeReleaseSpinLock(&Context->Lock, Irql); - return status; } -static VOID -GnttabPut( - IN PXENBUS_GNTTAB_CONTEXT Context, - IN ULONG Reference +static FORCEINLINE VOID +__GnttabEmpty( + IN PXENBUS_GNTTAB_CONTEXT Context ) { - KIRQL Irql; - PGNTTAB_DESCRIPTOR Descriptor; + ULONGLONG Entry; - ASSERT(!GNTTAB_IS_INVALID_REFERENCE(Reference)); + PoolTeardown(Context->DescriptorPool); + Context->DescriptorPool = NULL; - Descriptor = &Context->Descriptor[Reference]; + for (Entry = GNTTAB_RESERVED_ENTRY_COUNT; + Entry < Context->FrameCount * GNTTAB_ENTRY_PER_FRAME; + Entry++) { + NTSTATUS status; - Descriptor->Caller = NULL; - - KeAcquireSpinLock(&Context->Lock, &Irql); - - RemoveEntryList(&Descriptor->ListEntry); - RtlZeroMemory(&Descriptor->ListEntry, sizeof (LIST_ENTRY)); - - ASSERT(IsZeroMemory(Descriptor, sizeof (GNTTAB_DESCRIPTOR))); - - ASSERT(GNTTAB_IS_INVALID_REFERENCE(Descriptor->Next)); + status = RangeSetGet(Context->RangeSet, Entry); + ASSERT(NT_SUCCESS(status)); + } - *Context->TailFreeReference = Reference; + RangeSetTeardown(Context->RangeSet); + Context->RangeSet = NULL; +} - ASSERT(GNTTAB_IS_INVALID_REFERENCE(Descriptor->Next)); - Context->TailFreeReference = &Descriptor->Next; +static PXENBUS_GNTTAB_DESCRIPTOR +GnttabGet( + IN PXENBUS_GNTTAB_CONTEXT Context + ) +{ + return PoolGet(Context->DescriptorPool, FALSE); +} - KeReleaseSpinLock(&Context->Lock, Irql); +static VOID +GnttabPut( + IN PXENBUS_GNTTAB_CONTEXT Context, + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor + ) +{ + PoolPut(Context->DescriptorPool, Descriptor, FALSE); } static FORCEINLINE NTSTATUS __GnttabPermitForeignAccessFullPage( - IN PXENBUS_GNTTAB_CONTEXT Context, - IN ULONG Reference, - IN USHORT Domain, - IN va_list Arguments + IN PXENBUS_GNTTAB_CONTEXT Context, + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor, + IN USHORT Domain, + IN va_list Arguments ) { - PGNTTAB_DESCRIPTOR Descriptor; - PFN_NUMBER Frame; - BOOLEAN ReadOnly; - grant_entry_v1_t *Entry; - - ASSERT(!GNTTAB_IS_INVALID_REFERENCE(Reference)); + PFN_NUMBER Frame; + BOOLEAN ReadOnly; + grant_entry_v1_t *Entry; Frame = va_arg(Arguments, PFN_NUMBER); ReadOnly = va_arg(Arguments, BOOLEAN); - Descriptor = &Context->Descriptor[Reference]; ASSERT(IsZeroMemory(&Descriptor->Entry, sizeof (grant_entry_v1_t))); Descriptor->Entry.flags = (ReadOnly) ? GTF_readonly : 0; @@ -278,7 +310,7 @@ __GnttabPermitForeignAccessFullPage( Descriptor->Entry.frame = (uint32_t)Frame; ASSERT3U(Descriptor->Entry.frame, ==, Frame); - Entry = &Context->Entry[Reference]; + Entry = &Context->Entry[Descriptor->Reference]; *Entry = Descriptor->Entry; KeMemoryBarrier(); @@ -292,7 +324,7 @@ __GnttabPermitForeignAccessFullPage( static NTSTATUS GnttabPermitForeignAccess( IN PXENBUS_GNTTAB_CONTEXT Context, - IN ULONG Reference, + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor, IN USHORT Domain, IN XENBUS_GNTTAB_ENTRY_TYPE Type, ... @@ -304,7 +336,7 @@ GnttabPermitForeignAccess( va_start(Arguments, Type); switch (Type) { case GNTTAB_ENTRY_FULL_PAGE: - status =__GnttabPermitForeignAccessFullPage(Context, Reference, Domain, Arguments); + status =__GnttabPermitForeignAccessFullPage(Context, Descriptor, Domain, Arguments); break; default: @@ -318,17 +350,16 @@ GnttabPermitForeignAccess( static NTSTATUS GnttabRevokeForeignAccess( - IN PXENBUS_GNTTAB_CONTEXT Context, - IN ULONG Reference + IN PXENBUS_GNTTAB_CONTEXT Context, + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor ) { - grant_entry_v1_t *Entry; - volatile SHORT *Flags; - PGNTTAB_DESCRIPTOR Descriptor; - ULONG Attempt; - NTSTATUS status; + grant_entry_v1_t *Entry; + volatile SHORT *Flags; + ULONG Attempt; + NTSTATUS status; - Entry = &Context->Entry[Reference]; + Entry = &Context->Entry[Descriptor->Reference]; Flags = (volatile SHORT *)&Entry->flags; Attempt = 0; @@ -352,8 +383,6 @@ GnttabRevokeForeignAccess( goto fail1; RtlZeroMemory(Entry, sizeof (grant_entry_v1_t)); - - Descriptor = &Context->Descriptor[Reference]; RtlZeroMemory(&Descriptor->Entry, sizeof (grant_entry_v1_t)); return STATUS_SUCCESS; @@ -364,79 +393,15 @@ fail1: return status; } -static NTSTATUS -GnttabCopy( - IN PXENBUS_GNTTAB_CONTEXT Context, - IN PLIST_ENTRY List, - IN ULONG Count +static ULONG +GnttabReference( + IN PXENBUS_GNTTAB_CONTEXT Context, + IN PXENBUS_GNTTAB_DESCRIPTOR Descriptor ) { - struct gnttab_copy *op; - PLIST_ENTRY ListEntry; - ULONG Index; - NTSTATUS status; - UNREFERENCED_PARAMETER(Context); - op = __GnttabAllocate(sizeof (struct gnttab_copy) * Count); - - status = STATUS_NO_MEMORY; - if (op == NULL) - goto fail1; - - Index = 0; - for (ListEntry = List->Flink; - ListEntry != List; - ListEntry = ListEntry->Flink) { - PXENBUS_GNTTAB_COPY_OPERATION Operation; - - Operation = CONTAINING_RECORD(ListEntry, XENBUS_GNTTAB_COPY_OPERATION, ListEntry); - - ASSERT3U(Operation->Length, <=, PAGE_SIZE); - ASSERT3U(Operation->Offset + Operation->Length, <=, PAGE_SIZE); - ASSERT3U(Operation->RemoteOffset + Operation->Length, <=, PAGE_SIZE); - - op[Index].source.domid = Operation->RemoteDomain; - op[Index].source.u.ref = Operation->RemoteReference; - op[Index].source.offset = (USHORT)Operation->RemoteOffset; - - op[Index].dest.domid = DOMID_SELF; - op[Index].dest.u.gmfn = Operation->Pfn; - op[Index].dest.offset = (USHORT)Operation->Offset; - - op[Index].len = (USHORT)Operation->Length; - op[Index].flags = GNTCOPY_source_gref; - - Index++; - } - ASSERT3U(Index, ==, Count); - - status = GrantTableCopy(op, Count); - if (!NT_SUCCESS(status)) - goto fail2; - - status = STATUS_UNSUCCESSFUL; - for (Index = 0; Index < Count; Index++) { - if (op[Index].status != 0) - goto fail3; - } - - __GnttabFree(op); - - return STATUS_SUCCESS; - -fail3: - Error("fail3\n"); - -fail2: - Error("fail2\n"); - - __GnttabFree(op); - -fail1: - Error("fail1 (%08x)\n", status); - - return status; + return (ULONG)Descriptor->Reference; } static VOID @@ -513,6 +478,10 @@ GnttabDebugCallback( ) { PXENBUS_GNTTAB_CONTEXT Context = Argument; + ULONG Allocated; + ULONG MaximumAllocated; + ULONG Count; + ULONG MinimumCount; UNREFERENCED_PARAMETER(Crashing); @@ -527,6 +496,26 @@ GnttabDebugCallback( Context->DebugCallback, "FrameCount = %u\n", Context->FrameCount); + + PoolGetStatistics(Context->DescriptorPool, + &Allocated, + &MaximumAllocated, + &Count, + &MinimumCount); + + DEBUG(Printf, + Context->DebugInterface, + Context->DebugCallback, + "DESCRIPTOR POOL: Allocated = %u (Maximum = %u)\n", + Allocated, + MaximumAllocated); + + DEBUG(Printf, + Context->DebugInterface, + Context->DebugCallback, + "DESCRIPTOR POOL: Count = %u (Minimum = %u)\n", + Count, + MinimumCount); } NTSTATUS @@ -568,13 +557,9 @@ GnttabInitialize( Info("grant_entry_v1_t *: %p\n", Context->Entry); - InitializeListHead(&Context->List); KeInitializeSpinLock(&Context->Lock); - ASSERT(GNTTAB_IS_INVALID_REFERENCE(Context->HeadFreeReference)); - Context->TailFreeReference = &Context->HeadFreeReference; - - GnttabExpand(Context); + __GnttabFill(Context); Context->SuspendInterface = FdoGetSuspendInterface(Fdo); @@ -628,10 +613,7 @@ fail3: __GnttabEmpty(Context); - Context->TailFreeReference = NULL; - RtlZeroMemory(&Context->Lock, sizeof (KSPIN_LOCK)); - RtlZeroMemory(&Context->List, sizeof (LIST_ENTRY)); Context->Entry = NULL; @@ -663,9 +645,6 @@ GnttabTeardown( Trace("====>\n"); - if (!IsListEmpty(&Context->List)) - BUG("OUTSTANDING REFERENCES"); - DEBUG(Deregister, Context->DebugInterface, Context->DebugCallback); @@ -684,10 +663,7 @@ GnttabTeardown( __GnttabEmpty(Context); - Context->TailFreeReference = NULL; - RtlZeroMemory(&Context->Lock, sizeof (KSPIN_LOCK)); - RtlZeroMemory(&Context->List, sizeof (LIST_ENTRY)); Context->Entry = NULL; diff --git a/src/xenbus/pool.c b/src/xenbus/pool.c new file mode 100644 index 0000000..0c7c2e0 --- /dev/null +++ b/src/xenbus/pool.c @@ -0,0 +1,542 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include "pool.h" +#include "dbg_print.h" +#include "assert.h" + +#define POOL_POOL 'OBJE' + +typedef struct _OBJECT_HEADER { + ULONG Magic; + +#define OBJECT_HEADER_MAGIC 0x02121996 + + LIST_ENTRY ListEntry; +} OBJECT_HEADER, *POBJECT_HEADER; + +#define MAXIMUM_SLOTS 6 + +typedef struct _POOL_MAGAZINE { + PVOID Slot[MAXIMUM_SLOTS]; +} POOL_MAGAZINE, *PPOOL_MAGAZINE; + +struct _XENBUS_POOL { + const CHAR *Name; + ULONG Size; + NTSTATUS (*Ctor)(PVOID, PVOID); + VOID (*Dtor)(PVOID, PVOID); + VOID (*AcquireLock)(PVOID); + VOID (*ReleaseLock)(PVOID); + PVOID Argument; + KTIMER Timer; + KDPC Dpc; + LIST_ENTRY GetList; + PLIST_ENTRY PutList; + POOL_MAGAZINE Magazine[MAXIMUM_PROCESSORS]; + LONG Allocated; + LONG MaximumAllocated; + LONG Count; + LONG MinimumCount; +}; + +static FORCEINLINE PVOID +__PoolAllocate( + IN ULONG Length + ) +{ + return __AllocateNonPagedPoolWithTag(Length, POOL_POOL); +} + +static FORCEINLINE VOID +__PoolFree( + IN PVOID Buffer + ) +{ + __FreePoolWithTag(Buffer, POOL_POOL); +} + +static FORCEINLINE VOID +__PoolSwizzle( + IN PXENBUS_POOL Pool + ) +{ + PLIST_ENTRY ListEntry; + + ListEntry = InterlockedExchangePointer(&Pool->PutList, NULL); + + while (ListEntry != NULL) { + PLIST_ENTRY Next; + + Next = ListEntry->Flink; + ListEntry->Flink = NULL; + ASSERT3P(ListEntry->Blink, ==, NULL); + + InsertTailList(&Pool->GetList, ListEntry); + + ListEntry = Next; + } +} + +static FORCEINLINE PVOID +__PoolGetShared( + IN PXENBUS_POOL Pool, + IN BOOLEAN Locked + ) +{ + LONG Count; + POBJECT_HEADER Header; + PVOID Object; + LONG Allocated; + NTSTATUS status; + + Count = InterlockedDecrement(&Pool->Count); + + if (Count >= 0) { + PLIST_ENTRY ListEntry; + + if (!Locked) + Pool->AcquireLock(Pool->Argument); + + if (Count < Pool->MinimumCount) + Pool->MinimumCount = Count; + + if (IsListEmpty(&Pool->GetList)) + __PoolSwizzle(Pool); + + ListEntry = RemoveHeadList(&Pool->GetList); + ASSERT(ListEntry != &Pool->GetList); + + if (!Locked) + Pool->ReleaseLock(Pool->Argument); + + RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY)); + + Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry); + ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC); + + Object = Header + 1; + goto done; + } + + (VOID) InterlockedIncrement(&Pool->Count); + + Header = __PoolAllocate(sizeof (OBJECT_HEADER) + Pool->Size); + + status = STATUS_NO_MEMORY; + if (Header == NULL) + goto fail1; + + Header->Magic = OBJECT_HEADER_MAGIC; + + Object = Header + 1; + + status = Pool->Ctor(Pool->Argument, Object); + if (!NT_SUCCESS(status)) + goto fail2; + + Allocated = InterlockedIncrement(&Pool->Allocated); + + if (Allocated > Pool->MaximumAllocated) { + if (!Locked) + Pool->AcquireLock(Pool->Argument); + + if (Allocated > Pool->MaximumAllocated) + Pool->MaximumAllocated = Allocated; + + if (!Locked) + Pool->ReleaseLock(Pool->Argument); + } + +done: + return Object; + +fail2: + Error("fail2\n"); + + Header->Magic = 0; + + ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER))); + __PoolFree(Header); + +fail1: + Error("fail1 (%08x)\n", status); + + return NULL; +} + +static FORCEINLINE VOID +__PoolPutShared( + IN PXENBUS_POOL Pool, + IN PVOID Object, + IN BOOLEAN Locked + ) +{ + POBJECT_HEADER Header; + PLIST_ENTRY Old; + PLIST_ENTRY New; + + ASSERT(Object != NULL); + + Header = Object; + --Header; + ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC); + + ASSERT(IsZeroMemory(&Header->ListEntry, sizeof (LIST_ENTRY))); + + if (!Locked) { + New = &Header->ListEntry; + + do { + Old = Pool->PutList; + New->Flink = Old; + } while (InterlockedCompareExchangePointer(&Pool->PutList, New, Old) != Old); + } else { + InsertTailList(&Pool->GetList, &Header->ListEntry); + } + + KeMemoryBarrier(); + + (VOID) InterlockedIncrement(&Pool->Count); +} + +static FORCEINLINE PVOID +__PoolGetMagazine( + IN PXENBUS_POOL Pool, + IN ULONG Cpu + ) +{ + PPOOL_MAGAZINE Magazine; + ULONG Index; + + Magazine = &Pool->Magazine[Cpu]; + + for (Index = 0; Index < MAXIMUM_SLOTS; Index++) { + PVOID Object; + + if (Magazine->Slot[Index] != NULL) { + Object = Magazine->Slot[Index]; + Magazine->Slot[Index] = NULL; + + return Object; + } + } + + return NULL; +} + +static FORCEINLINE BOOLEAN +__PoolPutMagazine( + IN PXENBUS_POOL Pool, + IN ULONG Cpu, + IN PVOID Object + ) +{ + PPOOL_MAGAZINE Magazine; + ULONG Index; + + Magazine = &Pool->Magazine[Cpu]; + + for (Index = 0; Index < MAXIMUM_SLOTS; Index++) { + if (Magazine->Slot[Index] == NULL) { + Magazine->Slot[Index] = Object; + return TRUE; + } + } + + return FALSE; +} + +PVOID +PoolGet( + IN PXENBUS_POOL Pool, + IN BOOLEAN Locked + ) +{ + KIRQL Irql; + ULONG Cpu; + PVOID Object; + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + Cpu = KeGetCurrentProcessorNumber(); + + Object = __PoolGetMagazine(Pool, Cpu); + if (Object == NULL) + Object = __PoolGetShared(Pool, Locked); + + KeLowerIrql(Irql); + + return Object; +} + +VOID +PoolPut( + IN PXENBUS_POOL Pool, + IN PVOID Object, + IN BOOLEAN Locked + ) +{ + KIRQL Irql; + ULONG Cpu; + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + Cpu = KeGetCurrentProcessorNumber(); + + if (!__PoolPutMagazine(Pool, Cpu, Object)) + __PoolPutShared(Pool, Object, Locked); + + KeLowerIrql(Irql); +} + +VOID +PoolGetStatistics( + IN PXENBUS_POOL Pool, + OUT PULONG Allocated, + OUT PULONG MaximumAllocated, + OUT PULONG Count, + OUT PULONG MinimumCount + ) +{ + *Allocated = Pool->Allocated; + *MaximumAllocated = Pool->MaximumAllocated; + + *Count = Pool->Count; + *MinimumCount = Pool->MinimumCount; +} + +static FORCEINLINE +__PoolFlushMagazines( + IN PXENBUS_POOL Pool + ) +{ + ULONG Cpu; + + for (Cpu = 0; Cpu < MAXIMUM_PROCESSORS; Cpu++) { + PVOID Object; + + while ((Object = __PoolGetMagazine(Pool, Cpu)) != NULL) + __PoolPutShared(Pool, Object, TRUE); + } +} + +static FORCEINLINE VOID +__PoolTrimShared( + IN PXENBUS_POOL Pool, + IN OUT PLIST_ENTRY List + ) +{ + LONG Count; + LONG Excess; + + Count = Pool->Count; + + KeMemoryBarrier(); + + Excess = Pool->MinimumCount; + + while (Excess != 0) { + PLIST_ENTRY ListEntry; + + Count = InterlockedDecrement(&Pool->Count); + if (Count < 0) { + Count = InterlockedIncrement(&Pool->Count); + break; + } + + if (IsListEmpty(&Pool->GetList)) + __PoolSwizzle(Pool); + + ListEntry = RemoveHeadList(&Pool->GetList); + ASSERT(ListEntry != &Pool->GetList); + + InsertTailList(List, ListEntry); + + InterlockedDecrement(&Pool->Allocated); + --Excess; + } + + Pool->MinimumCount = Count; +} + +static FORCEINLINE VOID +__PoolEmpty( + IN PXENBUS_POOL Pool, + IN OUT PLIST_ENTRY List + ) +{ + while (!IsListEmpty(List)) { + PLIST_ENTRY ListEntry; + POBJECT_HEADER Header; + PVOID Object; + + ListEntry = RemoveHeadList(List); + RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY)); + + Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry); + ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC); + + Object = Header + 1; + + Pool->Dtor(Pool->Argument, Object); + + Header->Magic = 0; + + ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER))); + __PoolFree(Header); + } +} + +#define TIME_US(_us) ((_us) * 10) +#define TIME_MS(_ms) (TIME_US((_ms) * 1000)) +#define TIME_RELATIVE(_t) (-(_t)) + +#define POOL_PERIOD 1000 + +KDEFERRED_ROUTINE PoolDpc; + +VOID +PoolDpc( + IN PKDPC Dpc, + IN PVOID Context, + IN PVOID Argument1, + IN PVOID Argument2 + ) +{ + PXENBUS_POOL Pool = Context; + LIST_ENTRY List; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(Argument1); + UNREFERENCED_PARAMETER(Argument2); + + InitializeListHead(&List); + + Pool->AcquireLock(Pool->Argument); + __PoolTrimShared(Pool, &List); + Pool->ReleaseLock(Pool->Argument); + + __PoolEmpty(Pool, &List); + ASSERT(IsListEmpty(&List)); +} + +NTSTATUS +PoolInitialize( + IN const CHAR *Name, + IN ULONG Size, + IN NTSTATUS (*Ctor)(PVOID, PVOID), + IN VOID (*Dtor)(PVOID, PVOID), + IN VOID (*AcquireLock)(PVOID), + IN VOID (*ReleaseLock)(PVOID), + IN PVOID Argument, + OUT PXENBUS_POOL *Pool + ) +{ + LARGE_INTEGER Timeout; + NTSTATUS status; + + *Pool = __PoolAllocate(sizeof (XENBUS_POOL)); + + status = STATUS_NO_MEMORY; + if (*Pool == NULL) + goto fail1; + + (*Pool)->Name = Name; + (*Pool)->Size = Size; + (*Pool)->Ctor = Ctor; + (*Pool)->Dtor = Dtor; + (*Pool)->AcquireLock = AcquireLock; + (*Pool)->ReleaseLock = ReleaseLock; + (*Pool)->Argument = Argument; + + InitializeListHead(&(*Pool)->GetList); + + KeInitializeDpc(&(*Pool)->Dpc, + PoolDpc, + (*Pool)); + + Timeout.QuadPart = TIME_RELATIVE(TIME_MS(POOL_PERIOD)); + + KeInitializeTimer(&(*Pool)->Timer); + KeSetTimerEx(&(*Pool)->Timer, + Timeout, + POOL_PERIOD, + &(*Pool)->Dpc); + + return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +PoolTeardown( + IN PXENBUS_POOL Pool + ) +{ + LIST_ENTRY List; + + KeCancelTimer(&Pool->Timer); + KeFlushQueuedDpcs(); + + RtlZeroMemory(&Pool->Timer, sizeof (KTIMER)); + RtlZeroMemory(&Pool->Dpc, sizeof (KDPC)); + + InitializeListHead(&List); + + __PoolFlushMagazines(Pool); + + Pool->MinimumCount = Pool->Count; + __PoolTrimShared(Pool, &List); + __PoolEmpty(Pool, &List); + + ASSERT3U(Pool->Count, ==, 0); + ASSERT3U(Pool->Allocated, ==, 0); + Pool->MaximumAllocated = 0; + + RtlZeroMemory(&Pool->GetList, sizeof (LIST_ENTRY)); + + Pool->Argument = NULL; + Pool->ReleaseLock = NULL; + Pool->AcquireLock = NULL; + Pool->Dtor = NULL; + Pool->Ctor = NULL; + Pool->Size = 0; + Pool->Name = NULL; + + ASSERT(IsZeroMemory(Pool, sizeof (XENBUS_POOL))); + __PoolFree(Pool); +} diff --git a/src/xenbus/pool.h b/src/xenbus/pool.h new file mode 100644 index 0000000..89e3f6c --- /dev/null +++ b/src/xenbus/pool.h @@ -0,0 +1,78 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _XENBUS_POOL_H +#define _XENBUS_POOL_H + +#include + +typedef struct _XENBUS_POOL XENBUS_POOL, *PXENBUS_POOL; + +extern NTSTATUS +PoolInitialize( + IN const CHAR *Name, + IN ULONG Size, + IN NTSTATUS (*Ctor)(PVOID, PVOID), + IN VOID (*Dtor)(PVOID, PVOID), + IN VOID (*AcquireLock)(PVOID), + IN VOID (*ReleaseLock)(PVOID), + IN PVOID Argument, + OUT PXENBUS_POOL *Pool + ); + +extern VOID +PoolTeardown( + IN PXENBUS_POOL Pool + ); + +extern PVOID +PoolGet( + IN PXENBUS_POOL Pool, + IN BOOLEAN Locked + ); + +extern VOID +PoolPut( + IN PXENBUS_POOL Pool, + IN PVOID Object, + IN BOOLEAN Locked + ); + +extern VOID +PoolGetStatistics( + IN PXENBUS_POOL Pool, + OUT PULONG Allocated, + OUT PULONG MaximumAllocated, + OUT PULONG Count, + OUT PULONG MinimumCount + ); + +#endif // _XENBUS_POOL_H diff --git a/src/xenbus/range_set.c b/src/xenbus/range_set.c index 7e55b2d..24f63bd 100644 --- a/src/xenbus/range_set.c +++ b/src/xenbus/range_set.c @@ -179,6 +179,14 @@ __RangeSetMergeForwards( __RangeSetRemove(RangeSet, TRUE); } +BOOLEAN +RangeSetIsEmpty( + IN PXENBUS_RANGE_SET RangeSet + ) +{ + return IsListEmpty(&RangeSet->List); +} + ULONGLONG RangeSetPop( IN PXENBUS_RANGE_SET RangeSet diff --git a/src/xenbus/range_set.h b/src/xenbus/range_set.h index fadd476..80209da 100644 --- a/src/xenbus/range_set.h +++ b/src/xenbus/range_set.h @@ -36,6 +36,11 @@ typedef struct _XENBUS_RANGE_SET XENBUS_RANGE_SET, *PXENBUS_RANGE_SET; +extern BOOLEAN +RangeSetIsEmpty( + IN PXENBUS_RANGE_SET RangeSet + ); + extern ULONGLONG RangeSetPop( IN PXENBUS_RANGE_SET RangeSet