[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [win-pv-devel] [PATCH 12/14 v2] Implement ring protocol
From: Owen Smith <owen.smith@xxxxxxxxxx> Adds ring.h/.c which implements the console ring protocol and handles the cancel safe queues for the outstanding read/write IRPs. Connect the ring with the state protocol in the frontend. Also fixes frontend state transitions to get allow the ring to connect and operate correctly. Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx> --- src/xencons/frontend.c | 227 +++++---- src/xencons/ring.c | 1064 ++++++++++++++++++++++++++++++++++++++++ src/xencons/ring.h | 96 ++++ vs2015/xencons/xencons.vcxproj | 1 + vs2017/xencons/xencons.vcxproj | 1 + 5 files changed, 1306 insertions(+), 83 deletions(-) create mode 100755 src/xencons/ring.c create mode 100755 src/xencons/ring.h diff --git a/src/xencons/frontend.c b/src/xencons/frontend.c index a6e57d1..f7d3b23 100755 --- a/src/xencons/frontend.c +++ b/src/xencons/frontend.c @@ -44,6 +44,7 @@ #include "driver.h" #include "frontend.h" +#include "ring.h" #include "thread.h" #include "dbg_print.h" #include "assert.h" @@ -82,6 +83,8 @@ struct _XENCONS_FRONTEND { PXENBUS_SUSPEND_CALLBACK SuspendCallback; PXENBUS_DEBUG_CALLBACK DebugCallback; PXENBUS_STORE_WATCH Watch; + + PXENCONS_RING Ring; }; static const PCHAR @@ -592,11 +595,21 @@ FrontendClose( if (!FrontendIsOnline(Frontend)) break; - FrontendSetXenbusState(Frontend, - XenbusStateClosed); - FrontendWaitForBackendXenbusStateChange(Frontend, &State); + + switch (State) { + case XenbusStateClosing: + FrontendSetXenbusState(Frontend, + XenbusStateClosed); + break; + case XenbusStateClosed: + break; + default: + FrontendSetXenbusState(Frontend, + XenbusStateClosing); + break; + } } FrontendReleaseBackend(Frontend); @@ -631,11 +644,20 @@ FrontendPrepare( if (!FrontendIsOnline(Frontend)) break; - FrontendSetXenbusState(Frontend, - XenbusStateInitialising); - FrontendWaitForBackendXenbusStateChange(Frontend, &State); + switch (State) { + case XenbusStateInitWait: + break; + case XenbusStateClosed: + FrontendSetXenbusState(Frontend, + XenbusStateClosed); + break; + default: + FrontendSetXenbusState(Frontend, + XenbusStateInitialising); + break; + } } status = STATUS_UNSUCCESSFUL; @@ -725,7 +747,9 @@ FrontendConnect( if (!NT_SUCCESS(status)) goto fail2; - // TODO: Connect Ring + status = RingConnect(Frontend->Ring); + if (!NT_SUCCESS(status)) + goto fail3; Attempt = 0; do { @@ -737,7 +761,10 @@ FrontendConnect( if (!NT_SUCCESS(status)) break; - // TODO: StoreWrite Ring + status = RingStoreWrite(Frontend->Ring, + Transaction); + if (!NT_SUCCESS(status)) + goto abort; status = XENBUS_STORE(TransactionEnd, &Frontend->StoreInterface, @@ -748,12 +775,12 @@ FrontendConnect( continue; - //abort: - // (VOID)XENBUS_STORE(TransactionEnd, - // &Frontend->StoreInterface, - // Transaction, - // FALSE); - // break; + abort: + (VOID)XENBUS_STORE(TransactionEnd, + &Frontend->StoreInterface, + Transaction, + FALSE); + break; } while (status == STATUS_RETRY); if (!NT_SUCCESS(status)) @@ -764,11 +791,24 @@ FrontendConnect( if (!FrontendIsOnline(Frontend)) break; - FrontendSetXenbusState(Frontend, - XenbusStateConnected); - FrontendWaitForBackendXenbusStateChange(Frontend, &State); + + switch (State) { + case XenbusStateInitWait: + FrontendSetXenbusState(Frontend, + XenbusStateConnected); + break; + case XenbusStateConnected: + break; + case XenbusStateUnknown: + case XenbusStateClosing: + case XenbusStateClosed: + FrontendSetOffline(Frontend); + break; + default: + break; + } } status = STATUS_UNSUCCESSFUL; @@ -784,7 +824,7 @@ FrontendConnect( if (NT_SUCCESS(status)) { Length = (ULONG)strlen(Buffer); - Frontend->Name = __FrontendAllocate(Length); + Frontend->Name = __FrontendAllocate(Length + 1); if (Frontend->Name) RtlCopyMemory(Frontend->Name, Buffer, Length); @@ -802,7 +842,7 @@ FrontendConnect( if (NT_SUCCESS(status)) { Length = (ULONG)strlen(Buffer); - Frontend->Protocol = __FrontendAllocate(Length); + Frontend->Protocol = __FrontendAllocate(Length + 1); if (Frontend->Protocol) RtlCopyMemory(Frontend->Protocol, Buffer, Length); @@ -820,12 +860,21 @@ fail5: fail4: Error("fail4\n"); -//fail3: + RingDisconnect(Frontend->Ring); + +fail3: Error("fail3\n"); + XENBUS_DEBUG(Deregister, + &Frontend->DebugInterface, + Frontend->DebugCallback); + Frontend->DebugCallback = NULL; + fail2: Error("fail2\n"); + XENBUS_DEBUG(Release, &Frontend->DebugInterface); + fail1: Error("fail1 (%08x)\n", status); @@ -845,7 +894,7 @@ FrontendDisconnect( __FrontendFree(Frontend->Name); Frontend->Name = NULL; - // TODO: Disconnect Ring + RingDisconnect(Frontend->Ring); XENBUS_DEBUG(Deregister, &Frontend->DebugInterface, @@ -862,14 +911,22 @@ FrontendEnable( IN PXENCONS_FRONTEND Frontend ) { + NTSTATUS status; + Trace("====>\n"); - UNREFERENCED_PARAMETER(Frontend); - // TODO: Enable Ring + status = RingEnable(Frontend->Ring); + if (!NT_SUCCESS(status)) + goto fail1; Trace("<====\n"); return STATUS_SUCCESS; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; } static VOID @@ -879,8 +936,7 @@ FrontendDisable( { Trace("====>\n"); - UNREFERENCED_PARAMETER(Frontend); - // TODO: Disable Ring + RingDisable(Frontend->Ring); Trace("<====\n"); } @@ -1048,6 +1104,7 @@ __FrontendResume( ASSERT3U(Frontend->State, == , FRONTEND_UNKNOWN); UNREFERENCED_PARAMETER(Frontend); + // Current backends dont like re-opening after being closed //(VOID)FrontendSetState(Frontend, FRONTEND_CLOSED); } @@ -1059,7 +1116,7 @@ __FrontendSuspend( ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL); UNREFERENCED_PARAMETER(Frontend); - //(VOID)FrontendSetState(Frontend, FRONTEND_UNKNOWN); + (VOID)FrontendSetState(Frontend, FRONTEND_UNKNOWN); } static DECLSPEC_NOINLINE VOID @@ -1186,7 +1243,8 @@ FrontendDestroy( RtlZeroMemory(&Frontend->EjectEvent, sizeof(KEVENT)); - // TODO: Teardown Ring + RingDestroy(Frontend->Ring); + Frontend->Ring = NULL; RtlZeroMemory(&Frontend->StoreInterface, sizeof(XENBUS_STORE_INTERFACE)); @@ -1294,40 +1352,42 @@ FrontendAbiAcquire( ) { PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context; + LONG Count; KIRQL Irql; NTSTATUS status; - InterlockedIncrement(&Frontend->RefCount); + Count = InterlockedIncrement(&Frontend->RefCount); + if (Count == 2) { + KeRaiseIrql(DISPATCH_LEVEL, &Irql); - KeRaiseIrql(DISPATCH_LEVEL, &Irql); + status = XENBUS_SUSPEND(Acquire, &Frontend->SuspendInterface); + if (!NT_SUCCESS(status)) + goto fail1; - status = XENBUS_SUSPEND(Acquire, &Frontend->SuspendInterface); - if (!NT_SUCCESS(status)) - goto fail1; + __FrontendResume(Frontend); - __FrontendResume(Frontend); + status = XENBUS_SUSPEND(Register, + &Frontend->SuspendInterface, + SUSPEND_CALLBACK_LATE, + FrontendSuspendCallback, + Frontend, + &Frontend->SuspendCallback); + if (!NT_SUCCESS(status)) + goto fail2; - status = XENBUS_SUSPEND(Register, - &Frontend->SuspendInterface, - SUSPEND_CALLBACK_LATE, - FrontendSuspendCallback, - Frontend, - &Frontend->SuspendCallback); - if (!NT_SUCCESS(status)) - goto fail2; + KeLowerIrql(Irql); - KeLowerIrql(Irql); + KeClearEvent(&Frontend->EjectEvent); + ThreadWake(Frontend->EjectThread); - KeClearEvent(&Frontend->EjectEvent); - ThreadWake(Frontend->EjectThread); - - Trace("waiting for eject thread\n"); + Trace("waiting for eject thread\n"); - (VOID)KeWaitForSingleObject(&Frontend->EjectEvent, - Executive, - KernelMode, - FALSE, - NULL); + (VOID)KeWaitForSingleObject(&Frontend->EjectEvent, + Executive, + KernelMode, + FALSE, + NULL); + } return STATUS_SUCCESS; @@ -1358,31 +1418,31 @@ FrontendAbiRelease( KIRQL Irql; Count = InterlockedDecrement(&Frontend->RefCount); + if (Count == 1) { + KeRaiseIrql(DISPATCH_LEVEL, &Irql); - KeRaiseIrql(DISPATCH_LEVEL, &Irql); - - XENBUS_SUSPEND(Deregister, - &Frontend->SuspendInterface, - Frontend->SuspendCallback); - Frontend->SuspendCallback = NULL; - - __FrontendSuspend(Frontend); + XENBUS_SUSPEND(Deregister, + &Frontend->SuspendInterface, + Frontend->SuspendCallback); + Frontend->SuspendCallback = NULL; - XENBUS_SUSPEND(Release, &Frontend->SuspendInterface); + __FrontendSuspend(Frontend); - KeLowerIrql(Irql); + XENBUS_SUSPEND(Release, &Frontend->SuspendInterface); - KeClearEvent(&Frontend->EjectEvent); - ThreadWake(Frontend->EjectThread); + KeLowerIrql(Irql); - Trace("waiting for eject thread\n"); + KeClearEvent(&Frontend->EjectEvent); + ThreadWake(Frontend->EjectThread); - (VOID)KeWaitForSingleObject(&Frontend->EjectEvent, - Executive, - KernelMode, - FALSE, - NULL); + Trace("waiting for eject thread\n"); + (VOID)KeWaitForSingleObject(&Frontend->EjectEvent, + Executive, + KernelMode, + FALSE, + NULL); + } if (Count == 0) FrontendDestroy(Frontend); } @@ -1403,9 +1463,7 @@ FrontendAbiD3ToD0( { PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context; - UNREFERENCED_PARAMETER(Frontend); - //return FrontendSetState(Frontend, FRONTEND_ENABLED); - return STATUS_SUCCESS; + return FrontendSetState(Frontend, FRONTEND_ENABLED); } static VOID @@ -1416,7 +1474,7 @@ FrontendAbiD0ToD3( PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context; UNREFERENCED_PARAMETER(Frontend); - //(VOID) FrontendSetState(Frontend, FRONTEND_CLOSED); + (VOID) FrontendSetState(Frontend, FRONTEND_CLOSED); } static NTSTATUS @@ -1425,9 +1483,9 @@ FrontendAbiOpen( IN PFILE_OBJECT FileObject ) { - UNREFERENCED_PARAMETER(Context); - UNREFERENCED_PARAMETER(FileObject); - return STATUS_SUCCESS; + PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context; + + return RingOpen(Frontend->Ring, FileObject); } static NTSTATUS @@ -1436,9 +1494,9 @@ FrontendAbiClose( IN PFILE_OBJECT FileObject ) { - UNREFERENCED_PARAMETER(Context); - UNREFERENCED_PARAMETER(FileObject); - return STATUS_SUCCESS; + PXENCONS_FRONTEND Frontend = (PXENCONS_FRONTEND)Context; + + return RingClose(Frontend->Ring, FileObject); } static NTSTATUS @@ -1455,7 +1513,7 @@ FrontendAbiPutQueue( switch (StackLocation->MajorFunction) { case IRP_MJ_READ: case IRP_MJ_WRITE: - return STATUS_DEVICE_NOT_READY; + return RingPutQueue(Frontend->Ring, Irp); case IRP_MJ_DEVICE_CONTROL: return FrontendGetProperty(Frontend, Irp); @@ -1526,7 +1584,9 @@ FrontendCreate( FdoGetSuspendInterface(PdoGetFdo(Pdo), &Frontend->SuspendInterface); FdoGetStoreInterface(PdoGetFdo(Pdo), &Frontend->StoreInterface); - // TODO: Initialize Ring + status = RingCreate(Frontend, &Frontend->Ring); + if (!NT_SUCCESS(status)) + goto fail4; KeInitializeEvent(&Frontend->EjectEvent, NotificationEvent, FALSE); @@ -1546,9 +1606,10 @@ fail5: RtlZeroMemory(&Frontend->EjectEvent, sizeof(KEVENT)); - // TODO: Teardown Ring + RingDestroy(Frontend->Ring); + Frontend->Ring = NULL; -//fail4: +fail4: Error("fail4\n"); RtlZeroMemory(&Frontend->StoreInterface, diff --git a/src/xencons/ring.c b/src/xencons/ring.c new file mode 100755 index 0000000..20c4520 --- /dev/null +++ b/src/xencons/ring.c @@ -0,0 +1,1064 @@ +/* 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 <ntddk.h> +#include <ntstrsafe.h> +#include <stdlib.h> + +#include <xen.h> +#include <debug_interface.h> +#include <store_interface.h> +#include <gnttab_interface.h> +#include <evtchn_interface.h> + +#include "frontend.h" +#include "ring.h" +#include "names.h" +#include "dbg_print.h" +#include "assert.h" +#include "util.h" + +typedef struct _XENCONS_QUEUE { + IO_CSQ Csq; + LIST_ENTRY List; + KSPIN_LOCK Lock; +} XENCONS_QUEUE, *PXENCONS_QUEUE; + +struct _XENCONS_RING { + PXENCONS_FRONTEND Frontend; + BOOLEAN Connected; + BOOLEAN Enabled; + KSPIN_LOCK Lock; + PXENBUS_GNTTAB_CACHE GnttabCache; + struct xencons_interface *Shared; + PMDL Mdl; + PXENBUS_GNTTAB_ENTRY Entry; + KDPC Dpc; + ULONG Dpcs; + ULONG Events; + PXENBUS_EVTCHN_CHANNEL Channel; + XENBUS_GNTTAB_INTERFACE GnttabInterface; + XENBUS_STORE_INTERFACE StoreInterface; + XENBUS_EVTCHN_INTERFACE EvtchnInterface; + XENBUS_DEBUG_INTERFACE DebugInterface; + PXENBUS_DEBUG_CALLBACK DebugCallback; + XENCONS_QUEUE Read; + XENCONS_QUEUE Write; + ULONG BytesRead; + ULONG BytesWritten; +}; + +#define MAXNAMELEN 128 +#define XENCONS_RING_TAG 'GNIR' + +static FORCEINLINE PVOID +__RingAllocate( + IN ULONG Length + ) +{ + return __AllocatePoolWithTag(NonPagedPool, Length, XENCONS_RING_TAG); +} + +static FORCEINLINE VOID +__RingFree( + IN PVOID Buffer + ) +{ + __FreePoolWithTag(Buffer, XENCONS_RING_TAG); +} + +IO_CSQ_INSERT_IRP_EX RingCsqInsertIrpEx; + +NTSTATUS +RingCsqInsertIrpEx( + IN PIO_CSQ Csq, + IN PIRP Irp, + IN PVOID InsertContext OPTIONAL + ) +{ + BOOLEAN ReInsert = (BOOLEAN)(ULONG_PTR)InsertContext; + PXENCONS_QUEUE Queue; + + Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq); + + if (ReInsert) { + // This only occurs if the DPC de-queued the IRP but + // then found the console to be blocked. + InsertHeadList(&Queue->List, &Irp->Tail.Overlay.ListEntry); + } else { + InsertTailList(&Queue->List, &Irp->Tail.Overlay.ListEntry); + } + + return STATUS_PENDING; +} + +IO_CSQ_REMOVE_IRP RingCsqRemoveIrp; + +VOID +RingCsqRemoveIrp( + IN PIO_CSQ Csq, + IN PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Csq); + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + +IO_CSQ_PEEK_NEXT_IRP RingCsqPeekNextIrp; + +PIRP +RingCsqPeekNextIrp( + IN PIO_CSQ Csq, + IN PIRP Irp, + IN PVOID PeekContext OPTIONAL + ) +{ + PXENCONS_QUEUE Queue; + PLIST_ENTRY ListEntry; + PIRP NextIrp; + + Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq); + + ListEntry = (Irp == NULL) ? + Queue->List.Flink : + Irp->Tail.Overlay.ListEntry.Flink; + + while (ListEntry != &Queue->List) { + PIO_STACK_LOCATION StackLocation; + + NextIrp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry); + if (PeekContext == NULL) + return NextIrp; + + StackLocation = IoGetCurrentIrpStackLocation(NextIrp); + if (StackLocation->FileObject == (PFILE_OBJECT)PeekContext) + return NextIrp; + + ListEntry = ListEntry->Flink; + } + + return NULL; +} + +#pragma warning(push) +#pragma warning(disable:28167) // function changes IRQL + +IO_CSQ_ACQUIRE_LOCK RingCsqAcquireLock; + +VOID +RingCsqAcquireLock( + IN PIO_CSQ Csq, + OUT PKIRQL Irql + ) +{ + PXENCONS_QUEUE Queue; + + Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq); + + KeAcquireSpinLock(&Queue->Lock, Irql); +} + +IO_CSQ_RELEASE_LOCK RingCsqReleaseLock; + +VOID +RingCsqReleaseLock( + IN PIO_CSQ Csq, + IN KIRQL Irql + ) +{ + PXENCONS_QUEUE Queue; + + Queue = CONTAINING_RECORD(Csq, XENCONS_QUEUE, Csq); + + KeReleaseSpinLock(&Queue->Lock, Irql); +} + +#pragma warning(pop) + +IO_CSQ_COMPLETE_CANCELED_IRP RingCsqCompleteCanceledIrp; + +VOID +RingCsqCompleteCanceledIrp( + IN PIO_CSQ Csq, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + UCHAR MajorFunction; + + UNREFERENCED_PARAMETER(Csq); + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + MajorFunction = StackLocation->MajorFunction; + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + Trace("CANCELLED (%02x:%s)\n", + MajorFunction, + MajorFunctionName(MajorFunction)); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + +static FORCEINLINE VOID +__RingCancelRequests( + IN PXENCONS_RING Ring, + IN PFILE_OBJECT FileObject + ) +{ + for (;;) { + PIRP Irp; + + Irp = IoCsqRemoveNextIrp(&Ring->Read.Csq, FileObject); + if (Irp == NULL) + break; + + RingCsqCompleteCanceledIrp(&Ring->Read.Csq, Irp); + } + for (;;) { + PIRP Irp; + + Irp = IoCsqRemoveNextIrp(&Ring->Write.Csq, FileObject); + if (Irp == NULL) + break; + + RingCsqCompleteCanceledIrp(&Ring->Write.Csq, Irp); + } +} + +static VOID +RingAcquireLock( + IN PVOID Argument + ) +{ + PXENCONS_RING Ring = Argument; + + KeAcquireSpinLockAtDpcLevel(&Ring->Lock); +} + +static VOID +RingReleaseLock( + IN PVOID Argument + ) +{ + PXENCONS_RING Ring = Argument; + +#pragma prefast(suppress:26110) + KeReleaseSpinLockFromDpcLevel(&Ring->Lock); +} + +NTSTATUS +RingOpen( + IN PXENCONS_RING Ring, + IN PFILE_OBJECT FileObject + ) +{ + UNREFERENCED_PARAMETER(Ring); + UNREFERENCED_PARAMETER(FileObject); + return STATUS_SUCCESS; +} + +NTSTATUS +RingClose( + IN PXENCONS_RING Ring, + IN PFILE_OBJECT FileObject + ) +{ + __RingCancelRequests(Ring, FileObject); + return STATUS_SUCCESS; +} + +NTSTATUS +RingPutQueue( + IN PXENCONS_RING Ring, + IN PIRP Irp + ) +{ + PIO_STACK_LOCATION StackLocation; + NTSTATUS status; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + + switch (StackLocation->MajorFunction) { + case IRP_MJ_READ: + status = IoCsqInsertIrpEx(&Ring->Read.Csq, + Irp, + NULL, + (PVOID)FALSE); + break; + + case IRP_MJ_WRITE: + status = IoCsqInsertIrpEx(&Ring->Write.Csq, + Irp, + NULL, + (PVOID)FALSE); + break; + + default: + ASSERT(FALSE); + status = STATUS_NOT_SUPPORTED; // Keep SDV happy + break; + } + if (status != STATUS_PENDING) + goto fail1; + + KeInsertQueueDpc(&Ring->Dpc, NULL, NULL); + return STATUS_PENDING; + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +static ULONG +RingCopyFromRead( + IN PXENCONS_RING Ring, + IN PCHAR Data, + IN ULONG Length + ) +{ + struct xencons_interface *Shared; + XENCONS_RING_IDX cons; + XENCONS_RING_IDX prod; + ULONG Offset; + + Shared = Ring->Shared; + + KeMemoryBarrier(); + + cons = Shared->in_cons; + prod = Shared->in_prod; + + KeMemoryBarrier(); + + Offset = 0; + while (Length != 0) { + ULONG Available; + ULONG Index; + ULONG CopyLength; + + Available = prod - cons; + + if (Available == 0) + break; + + Index = MASK_XENCONS_IDX(cons, Shared->in); + + CopyLength = __min(Length, Available); + CopyLength = __min(CopyLength, sizeof(Shared->in) - Index); + + RtlCopyMemory(Data + Offset, &Shared->in[Index], CopyLength); + + Offset += CopyLength; + Length -= CopyLength; + + cons += CopyLength; + } + + KeMemoryBarrier(); + + Shared->in_cons = cons; + + KeMemoryBarrier(); + + return Offset; +} + +static ULONG +RingCopyToWrite( + IN PXENCONS_RING Ring, + IN PCHAR Data, + IN ULONG Length + ) +{ + struct xencons_interface *Shared; + XENCONS_RING_IDX cons; + XENCONS_RING_IDX prod; + ULONG Offset; + + Shared = Ring->Shared; + + KeMemoryBarrier(); + + prod = Shared->out_prod; + cons = Shared->out_cons; + + KeMemoryBarrier(); + + Offset = 0; + while (Length != 0) { + ULONG Available; + ULONG Index; + ULONG CopyLength; + + Available = cons + sizeof(Shared->out) - prod; + + if (Available == 0) + break; + + Index = MASK_XENCONS_IDX(prod, Shared->out); + + CopyLength = __min(Length, Available); + CopyLength = __min(CopyLength, sizeof(Shared->out) - Index); + + RtlCopyMemory(&Shared->out[Index], Data + Offset, CopyLength); + + Offset += CopyLength; + Length -= CopyLength; + + prod += CopyLength; + } + + KeMemoryBarrier(); + + Shared->out_prod = prod; + + KeMemoryBarrier(); + + return Offset; +} + +static BOOLEAN +RingPoll( + IN PXENCONS_RING Ring + ) +{ + PIRP Irp; + PIO_STACK_LOCATION StackLocation; + ULONG Length; + PCHAR Buffer; + NTSTATUS status; + + for (;;) { + ULONG Read; + + Irp = IoCsqRemoveNextIrp(&Ring->Read.Csq, NULL); + if (Irp == NULL) + break; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + ASSERT(StackLocation->MajorFunction == IRP_MJ_READ); + + Length = StackLocation->Parameters.Read.Length; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + Read = RingCopyFromRead(Ring, + Buffer, + Length); + if (Read == 0) { + status = IoCsqInsertIrpEx(&Ring->Read.Csq, + Irp, + NULL, + (PVOID)TRUE); + ASSERT(status == STATUS_PENDING); + break; + } + + Ring->BytesRead += Read; + + Irp->IoStatus.Information = Read; + Irp->IoStatus.Status = STATUS_SUCCESS; + + Trace("COMPLETE (READ) (%u bytes)\n", + Irp->IoStatus.Information); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + for (;;) { + ULONG Written; + + Irp = IoCsqRemoveNextIrp(&Ring->Write.Csq, NULL); + if (Irp == NULL) + break; + + StackLocation = IoGetCurrentIrpStackLocation(Irp); + ASSERT(StackLocation->MajorFunction == IRP_MJ_WRITE); + + Length = StackLocation->Parameters.Write.Length; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + Written = RingCopyToWrite(Ring, + Buffer, + Length); + if (Written == 0) { + status = IoCsqInsertIrpEx(&Ring->Write.Csq, + Irp, + NULL, + (PVOID)TRUE); + ASSERT(status == STATUS_PENDING); + break; + } + + Ring->BytesWritten += Written; + + Irp->IoStatus.Information = Written; + Irp->IoStatus.Status = STATUS_SUCCESS; + + Trace("COMPLETE (WRITE) (%u bytes)\n", + Irp->IoStatus.Information); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return FALSE; +} + +__drv_functionClass(KDEFERRED_ROUTINE) +__drv_maxIRQL(DISPATCH_LEVEL) +__drv_minIRQL(PASSIVE_LEVEL) +__drv_sameIRQL +static VOID +RingDpc( + IN PKDPC Dpc, + IN PVOID Context, + IN PVOID Argument1, + IN PVOID Argument2 + ) +{ + PXENCONS_RING Ring = Context; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(Argument1); + UNREFERENCED_PARAMETER(Argument2); + + ASSERT(Ring != NULL); + + for (;;) { + BOOLEAN Enabled; + BOOLEAN Retry; + KIRQL Irql; + + KeAcquireSpinLock(&Ring->Lock, &Irql); + Enabled = Ring->Enabled; + KeReleaseSpinLock(&Ring->Lock, Irql); + + if (!Enabled) + break; + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + Retry = RingPoll(Ring); + KeLowerIrql(Irql); + + if (!Retry) + break; + } + + (VOID) XENBUS_EVTCHN(Unmask, + &Ring->EvtchnInterface, + Ring->Channel, + FALSE, + FALSE); +} + +KSERVICE_ROUTINE RingEvtchnCallback; + +BOOLEAN +RingEvtchnCallback( + IN PKINTERRUPT InterruptObject, + IN PVOID Argument + ) +{ + PXENCONS_RING Ring = Argument; + + UNREFERENCED_PARAMETER(InterruptObject); + + ASSERT(Ring != NULL); + + Ring->Events++; + + if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL)) + Ring->Dpcs++; + + return TRUE; +} + +static VOID +RingDebugCallback( + IN PVOID Argument, + IN BOOLEAN Crashing + ) +{ + PXENCONS_RING Ring = Argument; + + UNREFERENCED_PARAMETER(Crashing); + + XENBUS_DEBUG(Printf, + &Ring->DebugInterface, + "0x%p [%s]\n", + Ring, + (Ring->Enabled) ? "ENABLED" : "DISABLED"); + + // Dump shared ring + XENBUS_DEBUG(Printf, + &Ring->DebugInterface, + "SHARED: in_cons = %u in_prod = %u out_cons = %u out_prod = %u\n", + Ring->Shared->in_cons, + Ring->Shared->in_prod, + Ring->Shared->out_cons, + Ring->Shared->out_prod); + + XENBUS_DEBUG(Printf, + &Ring->DebugInterface, + "BYTES: read = %u written = %u\n", + Ring->BytesRead, + Ring->BytesWritten); +} + +NTSTATUS +RingEnable( + IN PXENCONS_RING Ring + ) +{ + Trace("====>\n"); + + KeAcquireSpinLockAtDpcLevel(&Ring->Lock); + Ring->Enabled = TRUE; + KeReleaseSpinLockFromDpcLevel(&Ring->Lock); + + (VOID)KeInsertQueueDpc(&Ring->Dpc, NULL, NULL); + + Trace("<====\n"); + + return STATUS_SUCCESS; +} + +VOID +RingDisable( + IN PXENCONS_RING Ring + ) +{ + Trace("====>\n"); + + ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL); + + KeAcquireSpinLockAtDpcLevel(&Ring->Lock); + Ring->Enabled = FALSE; + KeReleaseSpinLockFromDpcLevel(&Ring->Lock); + + Trace("<====\n"); +} + +NTSTATUS +RingConnect( + IN PXENCONS_RING Ring + ) +{ + CHAR Name[MAXNAMELEN]; + NTSTATUS status; + + Trace("====>\n"); + + ASSERT(!Ring->Connected); + + status = XENBUS_DEBUG(Acquire, &Ring->DebugInterface); + if (!NT_SUCCESS(status)) + goto fail1; + + status = XENBUS_EVTCHN(Acquire, &Ring->EvtchnInterface); + if (!NT_SUCCESS(status)) + goto fail2; + + status = XENBUS_GNTTAB(Acquire, &Ring->GnttabInterface); + if (!NT_SUCCESS(status)) + goto fail3; + + status = XENBUS_STORE(Acquire, &Ring->StoreInterface); + if (!NT_SUCCESS(status)) + goto fail4; + + status = RtlStringCbPrintfA(Name, + sizeof(Name), + "console_%s_gnttab", + PdoGetName(FrontendGetPdo(Ring->Frontend))); + if (!NT_SUCCESS(status)) + goto fail5; + + status = XENBUS_GNTTAB(CreateCache, + &Ring->GnttabInterface, + Name, + 0, + RingAcquireLock, + RingReleaseLock, + Ring, + &Ring->GnttabCache); + if (!NT_SUCCESS(status)) + goto fail6; + + Ring->Mdl = __AllocatePage(); + + status = STATUS_NO_MEMORY; + if (Ring->Mdl == NULL) + goto fail7; + + ASSERT(Ring->Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA); + Ring->Shared = Ring->Mdl->MappedSystemVa; + ASSERT(Ring->Shared != NULL); + + status = XENBUS_GNTTAB(PermitForeignAccess, + &Ring->GnttabInterface, + Ring->GnttabCache, + TRUE, + FrontendGetBackendDomain(Ring->Frontend), + MmGetMdlPfnArray(Ring->Mdl)[0], + FALSE, + &Ring->Entry); + if (!NT_SUCCESS(status)) + goto fail8; + + Ring->Channel = XENBUS_EVTCHN(Open, + &Ring->EvtchnInterface, + XENBUS_EVTCHN_TYPE_UNBOUND, + RingEvtchnCallback, + Ring, + FrontendGetBackendDomain(Ring->Frontend), + TRUE); + + status = STATUS_UNSUCCESSFUL; + if (Ring->Channel == NULL) + goto fail9; + + (VOID)XENBUS_EVTCHN(Unmask, + &Ring->EvtchnInterface, + Ring->Channel, + FALSE, + TRUE); + + status = XENBUS_DEBUG(Register, + &Ring->DebugInterface, + __MODULE__ "|POLLER", + RingDebugCallback, + Ring, + &Ring->DebugCallback); + if (!NT_SUCCESS(status)) + goto fail10; + + Ring->Connected = TRUE; + + Trace("<====\n"); + return STATUS_SUCCESS; + +fail10: + Error("fail10\n"); + + Ring->Events = 0; + + XENBUS_EVTCHN(Close, + &Ring->EvtchnInterface, + Ring->Channel); + Ring->Channel = NULL; + +fail9: + Error("fail9\n"); + + (VOID)XENBUS_GNTTAB(RevokeForeignAccess, + &Ring->GnttabInterface, + Ring->GnttabCache, + TRUE, + Ring->Entry); + Ring->Entry = NULL; + +fail8: + Error("fail8\n"); + + RtlZeroMemory(Ring->Shared, PAGE_SIZE); + + Ring->Shared = NULL; + __FreePage(Ring->Mdl); + Ring->Mdl = NULL; + +fail7: + Error("fail7\n"); + + XENBUS_GNTTAB(DestroyCache, + &Ring->GnttabInterface, + Ring->GnttabCache); + Ring->GnttabCache = NULL; + +fail6: + Error("fail6\n"); + +fail5: + Error("fail5\n"); + + XENBUS_STORE(Release, &Ring->StoreInterface); + +fail4: + Error("fail4\n"); + + XENBUS_GNTTAB(Release, &Ring->GnttabInterface); + +fail3: + Error("fail3\n"); + + XENBUS_EVTCHN(Release, &Ring->EvtchnInterface); + +fail2: + Error("fail2\n"); + + XENBUS_DEBUG(Release, &Ring->DebugInterface); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +NTSTATUS +RingStoreWrite( + IN PXENCONS_RING Ring, + IN PVOID Transaction + ) +{ + ULONG Port; + ULONG GrantRef; + NTSTATUS status; + + Port = XENBUS_EVTCHN(GetPort, + &Ring->EvtchnInterface, + Ring->Channel); + + status = XENBUS_STORE(Printf, + &Ring->StoreInterface, + Transaction, + FrontendGetPath(Ring->Frontend), + "port", + "%u", + Port); + if (!NT_SUCCESS(status)) + goto fail1; + + GrantRef = XENBUS_GNTTAB(GetReference, + &Ring->GnttabInterface, + Ring->Entry); + + status = XENBUS_STORE(Printf, + &Ring->StoreInterface, + Transaction, + FrontendGetPath(Ring->Frontend), + "ring-ref", + "%u", + GrantRef); + if (!NT_SUCCESS(status)) + goto fail2; + + return STATUS_SUCCESS; + +fail2: + Error("fail2\n"); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +RingDisconnect( + IN PXENCONS_RING Ring + ) +{ + Trace("====>\n"); + + ASSERT(Ring->Connected); + Ring->Connected = FALSE; + + XENBUS_DEBUG(Deregister, + &Ring->DebugInterface, + Ring->DebugCallback); + Ring->DebugCallback = NULL; + + Ring->Dpcs = 0; + Ring->Events = 0; + Ring->BytesRead = 0; + Ring->BytesWritten = 0; + + XENBUS_EVTCHN(Close, + &Ring->EvtchnInterface, + Ring->Channel); + Ring->Channel = NULL; + + (VOID)XENBUS_GNTTAB(RevokeForeignAccess, + &Ring->GnttabInterface, + Ring->GnttabCache, + TRUE, + Ring->Entry); + Ring->Entry = NULL; + + RtlZeroMemory(Ring->Shared, PAGE_SIZE); + + Ring->Shared = NULL; + __FreePage(Ring->Mdl); + Ring->Mdl = NULL; + + XENBUS_GNTTAB(DestroyCache, + &Ring->GnttabInterface, + Ring->GnttabCache); + Ring->GnttabCache = NULL; + + XENBUS_STORE(Release, &Ring->StoreInterface); + + XENBUS_GNTTAB(Release, &Ring->GnttabInterface); + + XENBUS_EVTCHN(Release, &Ring->EvtchnInterface); + + XENBUS_DEBUG(Release, &Ring->DebugInterface); + + Trace("<====\n"); +} + +NTSTATUS +RingCreate( + IN PXENCONS_FRONTEND Frontend, + OUT PXENCONS_RING *Ring + ) +{ + NTSTATUS status; + + *Ring = __RingAllocate(sizeof(XENCONS_RING)); + + status = STATUS_NO_MEMORY; + if (*Ring == NULL) + goto fail1; + + (*Ring)->Frontend = Frontend; + + FdoGetDebugInterface(PdoGetFdo(FrontendGetPdo(Frontend)), + &(*Ring)->DebugInterface); + + FdoGetEvtchnInterface(PdoGetFdo(FrontendGetPdo(Frontend)), + &(*Ring)->EvtchnInterface); + + FdoGetGnttabInterface(PdoGetFdo(FrontendGetPdo(Frontend)), + &(*Ring)->GnttabInterface); + + FdoGetStoreInterface(PdoGetFdo(FrontendGetPdo(Frontend)), + &(*Ring)->StoreInterface); + + KeInitializeSpinLock(&(*Ring)->Lock); + + KeInitializeThreadedDpc(&(*Ring)->Dpc, RingDpc, *Ring); + + KeInitializeSpinLock(&(*Ring)->Read.Lock); + InitializeListHead(&(*Ring)->Read.List); + + status = IoCsqInitializeEx(&(*Ring)->Read.Csq, + RingCsqInsertIrpEx, + RingCsqRemoveIrp, + RingCsqPeekNextIrp, + RingCsqAcquireLock, + RingCsqReleaseLock, + RingCsqCompleteCanceledIrp); + if (!NT_SUCCESS(status)) + goto fail2; + + KeInitializeSpinLock(&(*Ring)->Write.Lock); + InitializeListHead(&(*Ring)->Write.List); + + status = IoCsqInitializeEx(&(*Ring)->Write.Csq, + RingCsqInsertIrpEx, + RingCsqRemoveIrp, + RingCsqPeekNextIrp, + RingCsqAcquireLock, + RingCsqReleaseLock, + RingCsqCompleteCanceledIrp); + if (!NT_SUCCESS(status)) + goto fail3; + + return STATUS_SUCCESS; + +fail3: + Error("fail3\n"); + + RtlZeroMemory(&(*Ring)->Write.List, sizeof(LIST_ENTRY)); + RtlZeroMemory(&(*Ring)->Write.Lock, sizeof(KSPIN_LOCK)); + + RtlZeroMemory(&(*Ring)->Read.Csq, sizeof(IO_CSQ)); + +fail2: + Error("fail2\n"); + + RtlZeroMemory(&(*Ring)->Read.List, sizeof(LIST_ENTRY)); + RtlZeroMemory(&(*Ring)->Read.Lock, sizeof(KSPIN_LOCK)); + +fail1: + Error("fail1 (%08x)\n", status); + + return status; +} + +VOID +RingDestroy( + IN PXENCONS_RING Ring + ) +{ + ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL); + + // Cancel all outstanding IRPs + __RingCancelRequests(Ring, NULL); + + ASSERT(IsListEmpty(&Ring->Read.List)); + ASSERT(IsListEmpty(&Ring->Write.List)); + + RtlZeroMemory(&Ring->Write.Csq, sizeof(IO_CSQ)); + + RtlZeroMemory(&Ring->Write.List, sizeof(LIST_ENTRY)); + RtlZeroMemory(&Ring->Write.Lock, sizeof(KSPIN_LOCK)); + + RtlZeroMemory(&Ring->Read.Csq, sizeof(IO_CSQ)); + + RtlZeroMemory(&Ring->Read.List, sizeof(LIST_ENTRY)); + RtlZeroMemory(&Ring->Read.Lock, sizeof(KSPIN_LOCK)); + + RtlZeroMemory(&Ring->Dpc, sizeof(KDPC)); + + RtlZeroMemory(&Ring->Lock, sizeof(KSPIN_LOCK)); + + RtlZeroMemory(&Ring->StoreInterface, + sizeof(XENBUS_STORE_INTERFACE)); + + RtlZeroMemory(&Ring->GnttabInterface, + sizeof(XENBUS_GNTTAB_INTERFACE)); + + RtlZeroMemory(&Ring->EvtchnInterface, + sizeof(XENBUS_EVTCHN_INTERFACE)); + + RtlZeroMemory(&Ring->DebugInterface, + sizeof(XENBUS_DEBUG_INTERFACE)); + + Ring->Frontend = NULL; + + ASSERT(IsZeroMemory(Ring, sizeof(XENCONS_RING))); + __RingFree(Ring); +} diff --git a/src/xencons/ring.h b/src/xencons/ring.h new file mode 100755 index 0000000..aca32a2 --- /dev/null +++ b/src/xencons/ring.h @@ -0,0 +1,96 @@ +/* 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 _XENCONS_RING_H +#define _XENCONS_RING_H + +#include <ntddk.h> + +#include "frontend.h" + +typedef struct _XENCONS_RING XENCONS_RING, *PXENCONS_RING; + +extern NTSTATUS +RingCreate( + IN PXENCONS_FRONTEND Frontend, + OUT PXENCONS_RING *Ring + ); + +extern VOID +RingDestroy( + IN PXENCONS_RING Ring + ); + +extern NTSTATUS +RingConnect( + IN PXENCONS_RING Ring + ); + +extern NTSTATUS +RingStoreWrite( + IN PXENCONS_RING Ring, + IN PVOID Transaction + ); + +extern VOID +RingDisconnect( + IN PXENCONS_RING Ring + ); + +extern NTSTATUS +RingEnable( + IN PXENCONS_RING Ring + ); + +extern VOID +RingDisable( + IN PXENCONS_RING Ring + ); + +extern NTSTATUS +RingOpen( + IN PXENCONS_RING Ring, + IN PFILE_OBJECT FileObject + ); + +extern NTSTATUS +RingClose( + IN PXENCONS_RING Ring, + IN PFILE_OBJECT FileObject + ); + +extern NTSTATUS +RingPutQueue( + IN PXENCONS_RING Ring, + IN PIRP Irp + ); + +#endif // _XENCONS_RING_H diff --git a/vs2015/xencons/xencons.vcxproj b/vs2015/xencons/xencons.vcxproj index 8846b8e..435ef21 100644 --- a/vs2015/xencons/xencons.vcxproj +++ b/vs2015/xencons/xencons.vcxproj @@ -71,6 +71,7 @@ <ClCompile Include="../../src/xencons/console.c" /> <ClCompile Include="../../src/xencons/stream.c" /> <ClCompile Include="../../src/xencons/frontend.c" /> + <ClCompile Include="../../src/xencons/ring.c" /> <ClCompile Include="../../src/xencons/thread.c" /> </ItemGroup> <ItemGroup> diff --git a/vs2017/xencons/xencons.vcxproj b/vs2017/xencons/xencons.vcxproj index 9811da0..defb11e 100644 --- a/vs2017/xencons/xencons.vcxproj +++ b/vs2017/xencons/xencons.vcxproj @@ -79,6 +79,7 @@ <ClCompile Include="../../src/xencons/console.c" /> <ClCompile Include="../../src/xencons/stream.c" /> <ClCompile Include="../../src/xencons/frontend.c" /> + <ClCompile Include="../../src/xencons/ring.c" /> <ClCompile Include="../../src/xencons/thread.c" /> </ItemGroup> <ItemGroup> -- 2.8.3 _______________________________________________ win-pv-devel mailing list win-pv-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/win-pv-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |