--- /dev/null
+/* 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.
+ */
+
+/*! \file stats_interface.h
+ \brief XENBUS STATS Interface
+
+ This interface provides access to XENBUS's object stats
+ implementation.
+*/
+
+#ifndef _XENBUS_STATS_INTERFACE_H
+#define _XENBUS_STATS_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_STATS_SET
+ \brief Handle to a set of statistics
+*/
+typedef struct _XENBUS_STATS_SET XENBUS_STATS_SET, *PXENBUS_STATS_SET;
+
+/*! \typedef XENBUS_STATS_ACQUIRE
+ \brief Acquire a reference to the STATS interface
+
+ \param Interface The interface header
+*/
+typedef NTSTATUS
+(*XENBUS_STATS_ACQUIRE)(
+ IN PINTERFACE Interface
+ );
+
+/*! \typedef XENBUS_STATS_RELEASE
+ \brief Release a reference to the STATS interface
+
+ \param Interface The interface header
+*/
+typedef VOID
+(*XENBUS_STATS_RELEASE)(
+ IN PINTERFACE Interface
+ );
+
+/*! \enum _XENBUS_STATS_TYPE
+ \brief Type of statistics value
+*/
+typedef enum _XENBUS_STATS_TYPE {
+ XENBUS_STATS_TYPE_INVALID = 0,
+ XENBUS_STATS_TYPE_LONG64, /*!< Signed 64-bit Integer */
+ XENBUS_STATS_TYPE_ULONG64, /*!< Unsigned 64-bit Integer */
+ XENBUS_STATS_TYPE_DOUBLE, /*!< Double-precision floating point */
+ XENBUS_STATS_TYPE_LPSTR, /*!< NUL-terminated ASCII string */
+} XENBUS_STATS_TYPE, *PXENBUS_STATS_TYPE;
+
+/*! \struct _XENBUS_STATS_NAME_TYPE_V1
+ \brief Name and type information for a statics value
+*/
+struct _XENBUS_STATS_NAME_TYPE_V1 {
+ /*! Type of value */
+ XENBUS_STATS_TYPE Type;
+ /*! Name of value */
+ const CHAR *Name;
+};
+
+typedef struct _XENBUS_STATS_NAME_TYPE_V1 XENBUS_STATS_NAME_TYPE, *PXENBUS_STATS_NAME_TYPE;
+
+/*! \typedef XENBUS_STATS_REQUEST
+ \brief Callback function to request an update to a set of statics
+
+ \param Argument An optional context argument passed to the function
+ \param Set The statistics set to be updated
+*/
+typedef VOID
+(*XENBUS_STATS_REQUEST)(
+ IN PVOID Argument,
+ IN PXENBUS_STATS_SET Set
+ );
+
+/*! \typedef XENBUS_STATS_CREATE_SET
+ \brief Create a set of statistics
+
+ \param Interface The interface header
+ \param ProviderName The name of statistics provider
+ \param SetName The name of the specific set being created
+ \param NameType An array of XENBUS_STATS_NAME_TYPE structures
+ \param Count The number of entries in the NameType array
+ \param Function A function called to request an update of the set
+ \param Argument An optional context argument passed to the function
+ \param Set A pointer to a set handle to the initialized
+*/
+typedef NTSTATUS
+(*XENBUS_STATS_CREATE_SET)(
+ IN PINTERFACE Interface,
+ IN PCHAR ProviderName,
+ IN PCHAR SetName,
+ IN PXENBUS_STATS_NAME_TYPE NameType,
+ IN ULONG Count,
+ IN XENBUS_STATS_REQUEST Function,
+ IN PVOID Argument OPTIONAL,
+ OUT PXENBUS_STATS_SET *Set
+ );
+
+/*! \typedef XENBUS_STATS_UPDATE_VALUE
+ \brief Update a statistics value
+
+ \param Interface The interface header
+ \param Set The set handle
+ \param Index The index of the value to be updated
+ \param Buffer A buffer containing the new value
+*/
+typedef VOID
+(*XENBUS_STATS_UPDATE_VALUE)(
+ IN PINTERFACE Interface,
+ IN PXENBUS_STATS_SET Set,
+ IN ULONG Index,
+ IN PVOID Buffer
+ );
+
+/*! \typedef XENBUS_STATS_DESTROY_SET
+ \brief Destroy a set of statistics
+
+ \param Interface The interface header
+ \param Set The set handle
+*/
+typedef VOID
+(*XENBUS_STATS_DESTROY_SET)(
+ IN PINTERFACE Interface,
+ IN PXENBUS_STATS_SET Set
+ );
+
+// {ED5733CC-A2AE-456E-A1CC-C1B2E8E4FEA1}
+DEFINE_GUID(GUID_XENBUS_STATS_INTERFACE,
+0xed5733cc, 0xa2ae, 0x456e, 0xa1, 0xcc, 0xc1, 0xb2, 0xe8, 0xe4, 0xfe, 0xa1);
+
+/*! \struct _XENBUS_STATS_INTERFACE_V1
+ \brief STATS interface version 1
+ \ingroup interfaces
+*/
+struct _XENBUS_STATS_INTERFACE_V1 {
+ INTERFACE Interface;
+ XENBUS_STATS_ACQUIRE StatsAcquire;
+ XENBUS_STATS_RELEASE StatsRelease;
+ XENBUS_STATS_CREATE_SET StatsCreateSet;
+ XENBUS_STATS_UPDATE_VALUE StatsUpdateValue;
+ XENBUS_STATS_DESTROY_SET StatsDestroySet;
+};
+
+typedef struct _XENBUS_STATS_INTERFACE_V1 XENBUS_STATS_INTERFACE, *PXENBUS_STATS_INTERFACE;
+
+/*! \def XENBUS_STATS
+ \brief Macro at assist in method invocation
+*/
+#define XENBUS_STATS(_Method, _Interface, ...) \
+ (_Interface)->Stats ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif // _WINDLL
+
+#define XENBUS_STATS_INTERFACE_VERSION_MIN 1
+#define XENBUS_STATS_INTERFACE_VERSION_MAX 1
+
+#endif // _XENBUS_STATS_INTERFACE_H
ULONG GnttabInterfaceVersion;
ULONG UnplugInterfaceVersion;
ULONG EmulatedInterfaceVersion;
+ ULONG StatsInterfaceVersion;
} XENBUS_PDO_REVISION, *PXENBUS_PDO_REVISION;
-#define DEFINE_REVISION(_N, _S, _SI, _E, _D, _ST, _R, _C, _G, _U, _EM) \
- { (_N), (_S), (_SI), (_E), (_D), (_ST), (_R), (_C), (_G), (_U), (_EM) }
+#define DEFINE_REVISION(_N, _S, _SI, _E, _D, _ST, _R, _C, _G, _U, _EM, _STA) \
+ { (_N), (_S), (_SI), (_E), (_D), (_ST), (_R), (_C), (_G), (_U), (_EM), (_STA) }
static XENBUS_PDO_REVISION PdoRevision[] = {
DEFINE_REVISION_TABLE
ASSERT(IMPLY(Index == ARRAYSIZE(PdoRevision) - 1,
Revision->EmulatedInterfaceVersion == XENFILT_EMULATED_INTERFACE_VERSION_MAX));
+ if (Revision->StatsInterfaceVersion != 0) {
+ ASSERT3U(Revision->StatsInterfaceVersion, >=, XENBUS_STATS_INTERFACE_VERSION_MIN);
+ ASSERT3U(Revision->StatsInterfaceVersion, <=, XENBUS_STATS_INTERFACE_VERSION_MAX);
+ ASSERT(IMPLY(Index == ARRAYSIZE(PdoRevision) - 1,
+ Revision->StatsInterfaceVersion == XENBUS_STATS_INTERFACE_VERSION_MAX));
+ }
+
ASSERT3U(Revision->Number >> 24, ==, MAJOR_VERSION);
Info("%08X -> "
"CACHE v%u "
"GNTTAB v%u "
"UNPLUG v%u "
- "EMULATED v%u\n",
+ "EMULATED v%u "
+ "STATS v%u\n",
Revision->Number,
Revision->SuspendInterfaceVersion,
Revision->SharedInfoInterfaceVersion,
Revision->CacheInterfaceVersion,
Revision->GnttabInterfaceVersion,
Revision->UnplugInterfaceVersion,
- Revision->EmulatedInterfaceVersion);
+ Revision->EmulatedInterfaceVersion,
+ Revision->StatsInterfaceVersion);
}
}
return status; \
} \
+DEFINE_PDO_QUERY_INTERFACE(Cache)
DEFINE_PDO_QUERY_INTERFACE(Debug)
-DEFINE_PDO_QUERY_INTERFACE(Suspend)
-DEFINE_PDO_QUERY_INTERFACE(SharedInfo)
DEFINE_PDO_QUERY_INTERFACE(Evtchn)
-DEFINE_PDO_QUERY_INTERFACE(Store)
-DEFINE_PDO_QUERY_INTERFACE(RangeSet)
-DEFINE_PDO_QUERY_INTERFACE(Cache)
DEFINE_PDO_QUERY_INTERFACE(Gnttab)
+DEFINE_PDO_QUERY_INTERFACE(RangeSet)
+DEFINE_PDO_QUERY_INTERFACE(SharedInfo)
+DEFINE_PDO_QUERY_INTERFACE(Stats)
+DEFINE_PDO_QUERY_INTERFACE(Store)
+DEFINE_PDO_QUERY_INTERFACE(Suspend)
DEFINE_PDO_QUERY_INTERFACE(Unplug)
struct _INTERFACE_ENTRY {
static struct _INTERFACE_ENTRY PdoInterfaceTable[] = {
{ &GUID_BUS_INTERFACE_STANDARD, "BUS_INTERFACE", PdoQueryBusInterface },
+ { &GUID_XENBUS_CACHE_INTERFACE, "CACHE_INTERFACE", PdoQueryCacheInterface },
{ &GUID_XENBUS_DEBUG_INTERFACE, "DEBUG_INTERFACE", PdoQueryDebugInterface },
- { &GUID_XENBUS_SUSPEND_INTERFACE, "SUSPEND_INTERFACE", PdoQuerySuspendInterface },
- { &GUID_XENBUS_SHARED_INFO_INTERFACE, "SHARED_INFO_INTERFACE", PdoQuerySharedInfoInterface },
{ &GUID_XENBUS_EVTCHN_INTERFACE, "EVTCHN_INTERFACE", PdoQueryEvtchnInterface },
- { &GUID_XENBUS_STORE_INTERFACE, "STORE_INTERFACE", PdoQueryStoreInterface },
- { &GUID_XENBUS_RANGE_SET_INTERFACE, "RANGE_SET_INTERFACE", PdoQueryRangeSetInterface },
- { &GUID_XENBUS_CACHE_INTERFACE, "CACHE_INTERFACE", PdoQueryCacheInterface },
{ &GUID_XENBUS_GNTTAB_INTERFACE, "GNTTAB_INTERFACE", PdoQueryGnttabInterface },
+ { &GUID_XENBUS_RANGE_SET_INTERFACE, "RANGE_SET_INTERFACE", PdoQueryRangeSetInterface },
+ { &GUID_XENBUS_SHARED_INFO_INTERFACE, "SHARED_INFO_INTERFACE", PdoQuerySharedInfoInterface },
+ { &GUID_XENBUS_STATS_INTERFACE, "STATS_INTERFACE", PdoQueryStatsInterface },
+ { &GUID_XENBUS_STORE_INTERFACE, "STORE_INTERFACE", PdoQueryStoreInterface },
+ { &GUID_XENBUS_SUSPEND_INTERFACE, "SUSPEND_INTERFACE", PdoQuerySuspendInterface },
{ &GUID_XENBUS_UNPLUG_INTERFACE, "UNPLUG_INTERFACE", PdoQueryUnplugInterface },
{ &GUID_XENFILT_EMULATED_INTERFACE, "EMULATED_INTERFACE", PdoDelegateIrp },
{ NULL, NULL, NULL }
--- /dev/null
+/* 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 <procgrp.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+
+#include "stats.h"
+#include "thread.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+#define XENBUS_STATS_SET_MAGIC 'TESS'
+
+#define MAXPATHLEN 128
+
+struct _XENBUS_STATS_SET {
+ ULONG Magic;
+ LIST_ENTRY ListEntry;
+ CHAR Path[MAXPATHLEN];
+ PMDL NameTypes;
+ PMDL Values;
+ ULONG Count;
+ PXENBUS_GNTTAB_ENTRY *Entry;
+ PXENBUS_EVTCHN_CHANNEL Channel;
+ XENBUS_STATS_REQUEST Function;
+ PVOID Argument;
+ PXENBUS_STORE_WATCH Watch;
+ USHORT MonitorDomain;
+ BOOLEAN Connected;
+ KDPC Dpc;
+ ULONG Events;
+ ULONG Dpcs;
+};
+
+struct _XENBUS_STATS_CONTEXT {
+ PXENBUS_FDO Fdo;
+ KSPIN_LOCK Lock;
+ LONG References;
+ XENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+ XENBUS_EVTCHN_INTERFACE EvtchnInterface;
+ XENBUS_GNTTAB_INTERFACE GnttabInterface;
+ PXENBUS_GNTTAB_CACHE GnttabCache;
+ XENBUS_STORE_INTERFACE StoreInterface;
+ XENBUS_SUSPEND_INTERFACE SuspendInterface;
+ PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
+ LIST_ENTRY List;
+ PXENBUS_THREAD ScanThread;
+};
+
+#define STATS_TAG 'TATS'
+
+static FORCEINLINE PVOID
+__StatsAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocatePoolWithTag(NonPagedPool, Length, STATS_TAG);
+}
+
+static FORCEINLINE VOID
+__StatsFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, STATS_TAG);
+}
+
+KSERVICE_ROUTINE TransmitterRingEvtchnCallback;
+
+BOOLEAN
+StatsEvtchnCallback(
+ IN PKINTERRUPT InterruptObject,
+ IN PVOID Argument
+ )
+{
+ PXENBUS_STATS_SET Set = Argument;
+
+ UNREFERENCED_PARAMETER(InterruptObject);
+
+ ASSERT(Set != NULL);
+
+ Set->Events++;
+
+ if (KeInsertQueueDpc(&Set->Dpc, NULL, NULL))
+ Set->Dpcs++;
+
+ return TRUE;
+}
+
+static NTSTATUS
+StatsStoreWrite(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ ULONG Pages;
+ ULONG Index;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ Pages = (Set->Count + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ for (Index = 0; Index < Pages; Index++) {
+ CHAR NameTypeNode[sizeof ("name-refXX")];
+ CHAR ValueNode[sizeof ("val-refXX")];
+
+ status = RtlStringCbPrintfA(NameTypeNode,
+ sizeof (NameTypeNode),
+ "name-ref%u",
+ Index);
+ ASSERT(NT_SUCCESS(status));
+
+ status = RtlStringCbPrintfA(ValueNode,
+ sizeof (ValueNode),
+ "val-ref%u",
+ Index);
+ ASSERT(NT_SUCCESS(status));
+
+ status = XENBUS_STORE(Printf,
+ &Context->StoreInterface,
+ NULL,
+ Set->Path,
+ NameTypeNode,
+ "%u",
+ XENBUS_GNTTAB(GetReference,
+ &Context->GnttabInterface,
+ Set->Entry[Index]));
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = XENBUS_STORE(Printf,
+ &Context->StoreInterface,
+ NULL,
+ Set->Path,
+ ValueNode,
+ "%u",
+ XENBUS_GNTTAB(GetReference,
+ &Context->GnttabInterface,
+ Set->Entry[Pages + Index]));
+ if (!NT_SUCCESS(status))
+ goto fail2;
+ }
+
+ status = XENBUS_STORE(Printf,
+ &Context->StoreInterface,
+ NULL,
+ Set->Path,
+ "event-channel",
+ "%u",
+ XENBUS_EVTCHN(GetPort,
+ &Context->EvtchnInterface,
+ Set->Channel));
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+StatsConnectSet(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ LONG Pages;
+ LONG Index;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ Pages = (Set->Count + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ for (Index = 0; Index < Pages; Index++) {
+ PFN_NUMBER Pfn;
+
+ Pfn = MmGetMdlPfnArray(Set->NameTypes)[Index];
+
+ status = XENBUS_GNTTAB(PermitForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->MonitorDomain,
+ Pfn,
+ TRUE,
+ &Set->Entry[Index]);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+ }
+
+ for (Index = 0; Index < Pages; Index++) {
+ PFN_NUMBER Pfn;
+
+ Pfn = MmGetMdlPfnArray(Set->Values)[Index];
+
+ status = XENBUS_GNTTAB(PermitForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->MonitorDomain,
+ Pfn,
+ TRUE,
+ &Set->Entry[Pages + Index]);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+ }
+
+ Set->Channel = XENBUS_EVTCHN(Open,
+ &Context->EvtchnInterface,
+ XENBUS_EVTCHN_TYPE_UNBOUND,
+ StatsEvtchnCallback,
+ Set,
+ Set->MonitorDomain,
+ FALSE);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Set->Channel == NULL)
+ goto fail3;
+
+ status = StatsStoreWrite(Context, Set);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ Set->Connected = TRUE;
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail4:
+ Error("fail4\n");
+
+ XENBUS_EVTCHN(Close,
+ &Context->EvtchnInterface,
+ Set->Channel);
+ Set->Channel = NULL;
+
+fail3:
+ Error("fail3\n");
+
+ Index = Pages;
+
+fail2:
+ Error("fail2\n");
+
+ while (--Index >= 0) {
+ (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->Entry[Pages + Index]);
+ Set->Entry[Pages + Index] = NULL;
+ }
+
+ Index = Pages;
+
+fail1:
+ Error("fail1 (%08x)", status);
+
+ while (--Index >= 0) {
+ (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->Entry[Index]);
+ Set->Entry[Index] = NULL;
+ }
+
+ return status;
+}
+
+static VOID
+StatsDisconnectSet(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ LONG Pages;
+ LONG Index;
+
+ Trace("====>\n");
+
+ Set->Connected = FALSE;
+
+ XENBUS_EVTCHN(Close,
+ &Context->EvtchnInterface,
+ Set->Channel);
+ Set->Channel = NULL;
+
+ Pages = (Set->Count + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ Index = Pages;
+
+ while (--Index >= 0) {
+ (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->Entry[Pages + Index]);
+ Set->Entry[Pages + Index] = NULL;
+ }
+
+ Index = Pages;
+
+ while (--Index >= 0) {
+ (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+ &Context->GnttabInterface,
+ Context->GnttabCache,
+ TRUE,
+ Set->Entry[Index]);
+ Set->Entry[Index] = NULL;
+ }
+
+ Trace("<====\n");
+}
+
+static NTSTATUS
+StatsScan(
+ IN PXENBUS_THREAD Self,
+ IN PVOID _Context
+ )
+{
+ PXENBUS_STATS_CONTEXT Context = _Context;
+ PKEVENT Event;
+ NTSTATUS status;
+
+ Info("====>\n");
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ KIRQL Irql;
+ PLIST_ENTRY ListEntry;
+ PCHAR Buffer;
+
+ Trace("waiting...\n");
+
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+
+ Trace("awake\n");
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ KeAcquireSpinLock(&Context->Lock, &Irql);
+
+ if (Context->References == 0)
+ goto loop;
+
+ for (ListEntry = Context->List.Flink;
+ ListEntry != &Context->List;
+ ListEntry = ListEntry->Flink) {
+ PXENBUS_STATS_SET Set;
+
+ Set = CONTAINING_RECORD(ListEntry, XENBUS_STATS_SET, ListEntry);
+
+ status = XENBUS_STORE(Read,
+ &Context->StoreInterface,
+ NULL,
+ Set->Path,
+ "monitor-id",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Set->MonitorDomain = DOMID_INVALID;
+ } else {
+ Set->MonitorDomain = (USHORT)strtol(Buffer, NULL, 10);
+
+ XENBUS_STORE(Free,
+ &Context->StoreInterface,
+ Buffer);
+ }
+
+ if (Set->MonitorDomain != DOMID_INVALID &&
+ !Set->Connected)
+ (VOID) StatsConnectSet(Context, Set);
+ else if (Set->MonitorDomain == DOMID_INVALID &&
+ Set->Connected)
+ StatsDisconnectSet(Context, Set);
+ }
+
+loop:
+ KeReleaseSpinLock(&Context->Lock, Irql);
+ }
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+StatsAddSet(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ KIRQL Irql;
+ NTSTATUS status;
+
+ status = XENBUS_STORE(Printf,
+ &Context->StoreInterface,
+ NULL,
+ NULL,
+ Set->Path,
+ "");
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = XENBUS_STORE(WatchAdd,
+ &Context->StoreInterface,
+ NULL,
+ Set->Path,
+ ThreadGetEvent(Context->ScanThread),
+ &Set->Watch);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ KeAcquireSpinLock(&Context->Lock, &Irql);
+ InsertTailList(&Context->List, &Set->ListEntry);
+ KeReleaseSpinLock(&Context->Lock, Irql);
+
+ ThreadWake(Context->ScanThread);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ (VOID) XENBUS_STORE(Remove,
+ &Context->StoreInterface,
+ NULL,
+ NULL,
+ Set->Path);
+
+fail1:
+ Error("fail1 (%08x)\n");
+
+ return status;
+}
+
+static VOID
+StatsRemoveSet(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Context->Lock, &Irql);
+
+ RemoveEntryList(&Set->ListEntry);
+ RtlZeroMemory(&Set->ListEntry, sizeof (LIST_ENTRY));
+
+ if (Set->Connected)
+ StatsDisconnectSet(Context, Set);
+
+ Set->MonitorDomain = 0;
+
+ KeReleaseSpinLock(&Context->Lock, Irql);
+
+ (VOID) XENBUS_STORE(WatchRemove,
+ &Context->StoreInterface,
+ Set->Watch);
+ Set->Watch = NULL;
+
+ (VOID) XENBUS_STORE(Remove,
+ &Context->StoreInterface,
+ NULL,
+ NULL,
+ Set->Path);
+}
+
+static
+_Function_class_(KDEFERRED_ROUTINE)
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_IRQL_requires_min_(DISPATCH_LEVEL)
+_IRQL_requires_(DISPATCH_LEVEL)
+_IRQL_requires_same_
+VOID
+StatsDpc(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID Argument1,
+ IN PVOID Argument2
+ )
+{
+ PXENBUS_STATS_SET Set = Context;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(Argument1);
+ UNREFERENCED_PARAMETER(Argument2);
+
+ ASSERT(Set != NULL);
+
+ Set->Function(Set->Argument, Set);
+}
+
+static NTSTATUS
+StatsCreateSet(
+ IN PINTERFACE Interface,
+ IN PCHAR ProviderName,
+ IN PCHAR SetName,
+ IN PXENBUS_STATS_NAME_TYPE NameType,
+ IN ULONG Count,
+ IN XENBUS_STATS_REQUEST Function,
+ IN PVOID Argument OPTIONAL,
+ OUT PXENBUS_STATS_SET *Set
+ )
+{
+ PXENBUS_STATS_CONTEXT Context = Interface->Context;
+ ULONG Pages;
+ struct xen_stats_name_type *name_type;
+ ULONG Index;
+ NTSTATUS status;
+
+ *Set = __StatsAllocate(sizeof (XENBUS_STATS_SET));
+
+ status = STATUS_NO_MEMORY;
+ if (*Set == NULL)
+ goto fail1;
+
+ status = RtlStringCbPrintfA((*Set)->Path,
+ sizeof ((*Set)->Path),
+ "stats/%s/%s",
+ ProviderName,
+ SetName);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ (*Set)->Count = Count;
+
+ Pages = (Count + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ (*Set)->NameTypes = __AllocatePages(Pages);
+
+ status = STATUS_NO_MEMORY;
+ if ((*Set)->NameTypes == NULL)
+ goto fail3;
+
+ name_type = MmGetSystemAddressForMdlSafe((*Set)->NameTypes,
+ NormalPagePriority);
+ ASSERT(name_type != NULL);
+
+ for (Index = 0; Index < Count; Index++) {
+ status = RtlStringCbPrintfA(name_type[Index].name,
+ sizeof (name_type[Index].name),
+ "%s",
+ NameType[Index].Name);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ switch (NameType[Index].Type) {
+ case XENBUS_STATS_TYPE_LONG64:
+ name_type[Index].type = XEN_STATS_TYPE_S64;
+ status = STATUS_SUCCESS;
+ break;
+ case XENBUS_STATS_TYPE_ULONG64:
+ name_type[Index].type = XEN_STATS_TYPE_U64;
+ status = STATUS_SUCCESS;
+ break;
+ case XENBUS_STATS_TYPE_DOUBLE:
+ name_type[Index].type = XEN_STATS_TYPE_DOUBLE;
+ status = STATUS_SUCCESS;
+ break;
+ case XENBUS_STATS_TYPE_LPSTR:
+ name_type[Index].type = XEN_STATS_TYPE_ASCII;
+ status = STATUS_SUCCESS;
+ break;
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail5;
+ }
+
+ (*Set)->Values = __AllocatePages(Pages);
+
+ status = STATUS_NO_MEMORY;
+ if ((*Set)->Values == NULL)
+ goto fail6;
+
+ (*Set)->Entry = __StatsAllocate(sizeof (PXENBUS_GNTTAB_ENTRY) *
+ Pages *
+ 2); // name-type-ref and val-ref
+
+ status = STATUS_NO_MEMORY;
+ if ((*Set)->Entry == NULL)
+ goto fail7;
+
+ (*Set)->Function = Function;
+ (*Set)->Argument = Argument;
+
+ KeInitializeDpc(&(*Set)->Dpc, StatsDpc, *Set);
+
+ status = StatsAddSet(Context, *Set);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ return STATUS_SUCCESS;
+
+fail8:
+ Error("fail8\n");
+
+ RtlZeroMemory(&(*Set)->Dpc, sizeof (KDPC));
+
+ (*Set)->Argument = NULL;
+ (*Set)->Function = NULL;
+
+ __StatsFree((*Set)->Entry);
+ (*Set)->Entry = NULL;
+
+fail7:
+ Error("fail7\n");
+
+ __FreePages((*Set)->Values);
+ (*Set)->Values = NULL;
+
+fail6:
+ Error("fail6\n");
+
+fail5:
+ Error("fail5\n");
+
+fail4:
+ Error("fail4\n");
+
+ __FreePages((*Set)->NameTypes);
+ (*Set)->NameTypes = NULL;
+
+fail3:
+ Error("fail3\n");
+
+ (*Set)->Count = 0;
+
+ RtlZeroMemory((*Set)->Path, sizeof ((*Set)->Path));
+
+fail2:
+ Error("fail2\n");
+
+ ASSERT(IsZeroMemory(*Set, sizeof (XENBUS_STATS_SET)));
+ __StatsFree(*Set);
+
+fail1:
+ Error("fail1 (%08x)\n");
+
+ return status;
+}
+
+static VOID
+StatsUpdateValue(
+ IN PINTERFACE Interface,
+ IN PXENBUS_STATS_SET Set,
+ IN ULONG Index,
+ IN PVOID Buffer
+ )
+{
+ struct xen_stats_name_type *name_type;
+ struct xen_stats_value *value;
+
+ UNREFERENCED_PARAMETER(Interface);
+
+ name_type = MmGetSystemAddressForMdlSafe(Set->NameTypes,
+ NormalPagePriority);
+ ASSERT(name_type != NULL);
+
+ value = MmGetSystemAddressForMdlSafe(Set->Values,
+ NormalPagePriority);
+ ASSERT(value != NULL);
+
+ switch (name_type[Index].type) {
+ case XEN_STATS_TYPE_S64:
+ RtlCopyMemory(&value[Index], Buffer, sizeof (LONG64));
+ break;
+
+ case XEN_STATS_TYPE_U64:
+ RtlCopyMemory(&value[Index], Buffer, sizeof (ULONG64));
+ break;
+
+ case XEN_STATS_TYPE_DOUBLE:
+ RtlCopyMemory(&value[Index], Buffer, sizeof (DOUBLE));
+ break;
+
+ case XEN_STATS_TYPE_ASCII:
+ (VOID) RtlStringCbPrintfA((PCHAR)value[Index].buffer,
+ sizeof (struct xen_stats_value),
+ "%s",
+ Buffer);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static VOID
+StatsDestroySet(
+ IN PINTERFACE Interface,
+ IN PXENBUS_STATS_SET Set
+ )
+{
+ PXENBUS_STATS_CONTEXT Context = Interface->Context;
+
+ StatsRemoveSet(Context, Set);
+
+ RtlZeroMemory(&Set->Dpc, sizeof (KDPC));
+
+ Set->Argument = NULL;
+ Set->Function = NULL;
+
+ __StatsFree(Set->Entry);
+ Set->Entry = NULL;
+
+ __FreePages(Set->Values);
+ Set->Values = NULL;
+
+ __FreePages(Set->NameTypes);
+ Set->NameTypes = NULL;
+
+ Set->Count = 0;
+
+ RtlZeroMemory(Set->Path, sizeof (Set->Path));
+
+ ASSERT(IsZeroMemory(Set, sizeof (XENBUS_STATS_SET)));
+ __StatsFree(Set);
+}
+
+static VOID
+StatsDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ UNREFERENCED_PARAMETER(Argument);
+ UNREFERENCED_PARAMETER(Crashing);
+}
+
+static VOID
+StatsSuspendCallbackLate(
+ IN PVOID Argument
+ )
+{
+ UNREFERENCED_PARAMETER(Argument);
+}
+
+static VOID
+StatsAcquireLock(
+ IN PXENBUS_STATS_CONTEXT Context
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ KeAcquireSpinLockAtDpcLevel(&Context->Lock);
+}
+
+static VOID
+StatsReleaseLock(
+ IN PXENBUS_STATS_CONTEXT Context
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ KeReleaseSpinLockFromDpcLevel(&Context->Lock);
+}
+
+static NTSTATUS
+StatsAcquire(
+ IN PINTERFACE Interface
+ )
+{
+ PXENBUS_STATS_CONTEXT Context = Interface->Context;
+ KIRQL Irql;
+ NTSTATUS status;
+
+ KeAcquireSpinLock(&Context->Lock, &Irql);
+
+ if (Context->References++ != 0)
+ goto done;
+
+ Trace("====>\n");
+
+ status = XENBUS_DEBUG(Acquire, &Context->DebugInterface);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = XENBUS_DEBUG(Register,
+ &Context->DebugInterface,
+ __MODULE__ "|STATS",
+ StatsDebugCallback,
+ Context,
+ &Context->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = XENBUS_EVTCHN(Acquire, &Context->EvtchnInterface);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = XENBUS_GNTTAB(Acquire, &Context->GnttabInterface);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = XENBUS_GNTTAB(CreateCache,
+ &Context->GnttabInterface,
+ "stats",
+ 0,
+ StatsAcquireLock,
+ StatsReleaseLock,
+ Context,
+ &Context->GnttabCache);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ status = XENBUS_STORE(Acquire, &Context->StoreInterface);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ status = XENBUS_SUSPEND(Acquire, &Context->SuspendInterface);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+
+ status = XENBUS_SUSPEND(Register,
+ &Context->SuspendInterface,
+ SUSPEND_CALLBACK_LATE,
+ StatsSuspendCallbackLate,
+ Context,
+ &Context->SuspendCallbackLate);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ Trace("<====\n");
+
+done:
+ KeReleaseSpinLock(&Context->Lock, Irql);
+
+ return STATUS_SUCCESS;
+
+fail8:
+ Error("fail8\n");
+
+ XENBUS_SUSPEND(Release, &Context->SuspendInterface);
+
+fail7:
+ Error("fail7\n");
+
+ XENBUS_STORE(Release, &Context->StoreInterface);
+
+fail6:
+ Error("fail6\n");
+
+ XENBUS_GNTTAB(DestroyCache,
+ &Context->GnttabInterface,
+ Context->GnttabCache);
+ Context->GnttabCache = NULL;
+
+fail5:
+ Error("fail5\n");
+
+ XENBUS_GNTTAB(Release, &Context->GnttabInterface);
+
+fail4:
+ Error("fail4\n");
+
+ XENBUS_EVTCHN(Release, &Context->EvtchnInterface);
+
+fail3:
+ Error("fail3\n");
+
+ XENBUS_DEBUG(Deregister,
+ &Context->DebugInterface,
+ Context->DebugCallback);
+ Context->DebugCallback = NULL;
+
+fail2:
+ Error("fail2\n");
+
+ XENBUS_DEBUG(Release, &Context->DebugInterface);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ --Context->References;
+ ASSERT3U(Context->References, ==, 0);
+ KeReleaseSpinLock(&Context->Lock, Irql);
+
+ return status;
+}
+
+VOID
+StatsRelease(
+ IN PINTERFACE Interface
+ )
+{
+ PXENBUS_STATS_CONTEXT Context = Interface->Context;
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Context->Lock, &Irql);
+
+ if (--Context->References > 0)
+ goto done;
+
+ Trace("====>\n");
+
+ if (!IsListEmpty(&Context->List))
+ BUG("OUTSTANDING PROVIDERS");
+
+ XENBUS_SUSPEND(Deregister,
+ &Context->SuspendInterface,
+ Context->SuspendCallbackLate);
+ Context->SuspendCallbackLate = NULL;
+
+ XENBUS_SUSPEND(Release, &Context->SuspendInterface);
+
+ XENBUS_STORE(Release, &Context->StoreInterface);
+
+ XENBUS_GNTTAB(DestroyCache,
+ &Context->GnttabInterface,
+ Context->GnttabCache);
+ Context->GnttabCache = NULL;
+
+ XENBUS_GNTTAB(Release, &Context->GnttabInterface);
+
+ XENBUS_EVTCHN(Release, &Context->EvtchnInterface);
+
+ XENBUS_DEBUG(Deregister,
+ &Context->DebugInterface,
+ Context->DebugCallback);
+ Context->DebugCallback = NULL;
+
+ XENBUS_DEBUG(Release, &Context->DebugInterface);
+
+ Trace("<====\n");
+
+done:
+ KeReleaseSpinLock(&Context->Lock, Irql);
+}
+
+static struct _XENBUS_STATS_INTERFACE_V1 StatsInterfaceVersion1 = {
+ { sizeof (struct _XENBUS_STATS_INTERFACE_V1), 1, NULL, NULL, NULL },
+ StatsAcquire,
+ StatsRelease,
+ StatsCreateSet,
+ StatsUpdateValue,
+ StatsDestroySet
+};
+
+NTSTATUS
+StatsInitialize(
+ IN PXENBUS_FDO Fdo,
+ OUT PXENBUS_STATS_CONTEXT *Context
+ )
+{
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ *Context = __StatsAllocate(sizeof (XENBUS_STATS_CONTEXT));
+
+ status = STATUS_NO_MEMORY;
+ if (*Context == NULL)
+ goto fail1;
+
+ status = DebugGetInterface(FdoGetDebugContext(Fdo),
+ XENBUS_DEBUG_INTERFACE_VERSION_MAX,
+ (PINTERFACE)&(*Context)->DebugInterface,
+ sizeof ((*Context)->DebugInterface));
+ ASSERT(NT_SUCCESS(status));
+ ASSERT((*Context)->DebugInterface.Interface.Context != NULL);
+
+ status = EvtchnGetInterface(FdoGetEvtchnContext(Fdo),
+ XENBUS_EVTCHN_INTERFACE_VERSION_MAX,
+ (PINTERFACE)&(*Context)->EvtchnInterface,
+ sizeof ((*Context)->EvtchnInterface));
+ ASSERT(NT_SUCCESS(status));
+ ASSERT((*Context)->EvtchnInterface.Interface.Context != NULL);
+
+ status = GnttabGetInterface(FdoGetGnttabContext(Fdo),
+ XENBUS_GNTTAB_INTERFACE_VERSION_MAX,
+ (PINTERFACE)&(*Context)->GnttabInterface,
+ sizeof ((*Context)->GnttabInterface));
+ ASSERT(NT_SUCCESS(status));
+ ASSERT((*Context)->GnttabInterface.Interface.Context != NULL);
+
+ status = StoreGetInterface(FdoGetStoreContext(Fdo),
+ XENBUS_STORE_INTERFACE_VERSION_MAX,
+ (PINTERFACE)&(*Context)->StoreInterface,
+ sizeof ((*Context)->StoreInterface));
+ ASSERT(NT_SUCCESS(status));
+ ASSERT((*Context)->StoreInterface.Interface.Context != NULL);
+
+ status = SuspendGetInterface(FdoGetSuspendContext(Fdo),
+ XENBUS_SUSPEND_INTERFACE_VERSION_MAX,
+ (PINTERFACE)&(*Context)->SuspendInterface,
+ sizeof ((*Context)->SuspendInterface));
+ ASSERT(NT_SUCCESS(status));
+ ASSERT((*Context)->SuspendInterface.Interface.Context != NULL);
+
+ InitializeListHead(&(*Context)->List);
+ KeInitializeSpinLock(&(*Context)->Lock);
+
+ status = ThreadCreate(StatsScan, *Context, &(*Context)->ScanThread);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ (*Context)->Fdo = Fdo;
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ RtlZeroMemory(&(*Context)->Lock, sizeof (KSPIN_LOCK));
+ RtlZeroMemory(&(*Context)->List, sizeof (LIST_ENTRY));
+
+ RtlZeroMemory(&(*Context)->SuspendInterface,
+ sizeof (XENBUS_SUSPEND_INTERFACE));
+
+ RtlZeroMemory(&(*Context)->StoreInterface,
+ sizeof (XENBUS_STORE_INTERFACE));
+
+ RtlZeroMemory(&(*Context)->GnttabInterface,
+ sizeof (XENBUS_GNTTAB_INTERFACE));
+
+ RtlZeroMemory(&(*Context)->EvtchnInterface,
+ sizeof (XENBUS_EVTCHN_INTERFACE));
+
+ RtlZeroMemory(&(*Context)->DebugInterface,
+ sizeof (XENBUS_DEBUG_INTERFACE));
+
+ ASSERT(IsZeroMemory((*Context), sizeof (XENBUS_STATS_CONTEXT)));
+ __StatsFree(*Context);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+StatsGetInterface(
+ IN PXENBUS_STATS_CONTEXT Context,
+ IN ULONG Version,
+ IN OUT PINTERFACE Interface,
+ IN ULONG Size
+ )
+{
+ NTSTATUS status;
+
+ ASSERT(Context != NULL);
+
+ switch (Version) {
+ case 1: {
+ struct _XENBUS_STATS_INTERFACE_V1 *StatsInterface;
+
+ StatsInterface = (struct _XENBUS_STATS_INTERFACE_V1 *)Interface;
+
+ status = STATUS_BUFFER_OVERFLOW;
+ if (Size < sizeof (struct _XENBUS_STATS_INTERFACE_V1))
+ break;
+
+ *StatsInterface = StatsInterfaceVersion1;
+
+ ASSERT3U(Interface->Version, ==, Version);
+ Interface->Context = Context;
+
+ status = STATUS_SUCCESS;
+ break;
+ }
+ default:
+ status = STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ return status;
+}
+
+ULONG
+StatsGetReferences(
+ IN PXENBUS_STATS_CONTEXT Context
+ )
+{
+ return Context->References;
+}
+
+VOID
+StatsTeardown(
+ IN PXENBUS_STATS_CONTEXT Context
+ )
+{
+ Trace("====>\n");
+
+ Context->Fdo = NULL;
+
+ ThreadAlert(Context->ScanThread);
+ ThreadJoin(Context->ScanThread);
+ Context->ScanThread = NULL;
+
+ RtlZeroMemory(&Context->Lock, sizeof (KSPIN_LOCK));
+ RtlZeroMemory(&Context->List, sizeof (LIST_ENTRY));
+
+ RtlZeroMemory(&Context->SuspendInterface,
+ sizeof (XENBUS_SUSPEND_INTERFACE));
+
+ RtlZeroMemory(&Context->StoreInterface,
+ sizeof (XENBUS_STORE_INTERFACE));
+
+ RtlZeroMemory(&Context->GnttabInterface,
+ sizeof (XENBUS_GNTTAB_INTERFACE));
+
+ RtlZeroMemory(&Context->EvtchnInterface,
+ sizeof (XENBUS_EVTCHN_INTERFACE));
+
+ RtlZeroMemory(&Context->DebugInterface,
+ sizeof (XENBUS_DEBUG_INTERFACE));
+
+ ASSERT(IsZeroMemory(Context, sizeof (XENBUS_STATS_CONTEXT)));
+ __StatsFree(Context);
+
+ Trace("<====\n");
+}