]> xenbits.xensource.com Git - people/pauldu/xenbus.git/commitdiff
Add stats boilerplate stats
authorPaul Durrant <paul.durrant@citrix.com>
Fri, 25 Nov 2016 15:39:18 +0000 (15:39 +0000)
committerPaul Durrant <paul.durrant@citrix.com>
Wed, 30 Nov 2016 13:35:36 +0000 (13:35 +0000)
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
17 files changed:
include/revision.h
include/stats_interface.h [new file with mode: 0644]
include/suspend_interface.h
include/xen.h
include/xen/public/io/xen_stats.h [new file with mode: 0644]
src/coinst/coinst.c
src/common/util.h
src/xen/driver.c
src/xenbus.inf
src/xenbus/cache.c
src/xenbus/fdo.c
src/xenbus/fdo.h
src/xenbus/pdo.c
src/xenbus/stats.c [new file with mode: 0644]
src/xenbus/stats.h [new file with mode: 0644]
vs2012/xenbus/xenbus.vcxproj
vs2013/xenbus/xenbus.vcxproj

index 47db033f4a2e26c384e6d1c4ce74626b81009641..0aab21401e1b5576407996fb21c33baa3bb35df4 100644 (file)
 #define _REVISION_H
 
 // Key:
-// S  - XENBUS_SUSPEND_INTERFACE
-// SI - XENBUS_SHARED_INFO_INTERFACE
-// E  - XENBUS_EVTCHN_INTERFACE
-// D  - XENBUS_DEBUG_INTEFACE
-// ST - XENBUS_STORE_INTERFACE
-// R  - XENBUS_RANGE_SET_INTERFACE
-// C  - XENBUS_CACHE_INTERFACE
-// G  - XENBUS_GNTTAB_INTERFACE
-// EM - XENFILT_EMULATED_INTERFACE
+// S   - XENBUS_SUSPEND_INTERFACE
+// SI  - XENBUS_SHARED_INFO_INTERFACE
+// E   - XENBUS_EVTCHN_INTERFACE
+// D   - XENBUS_DEBUG_INTEFACE
+// ST  - XENBUS_STORE_INTERFACE
+// R   - XENBUS_RANGE_SET_INTERFACE
+// C   - XENBUS_CACHE_INTERFACE
+// G   - XENBUS_GNTTAB_INTERFACE
+// EM  - XENFILT_EMULATED_INTERFACE
+// STA - XENBUS_STATS_INTERFACE
 
-//                    REVISION   S  SI   E   D  ST   R   C   G   U  EM
-#define DEFINE_REVISION_TABLE                                               \
-    DEFINE_REVISION(0x08000009,  1,  2,  4,  1,  1,  1,  1,  1,  1,  1),    \
-    DEFINE_REVISION(0x0800000A,  1,  2,  5,  1,  1,  1,  1,  1,  1,  1),    \
-    DEFINE_REVISION(0x0800000B,  1,  2,  5,  1,  2,  1,  1,  2,  1,  1)
+//                    REVISION   S  SI   E   D  ST   R   C   G   U  EM STA
+#define DEFINE_REVISION_TABLE                                                \
+    DEFINE_REVISION(0x08000009,  1,  2,  4,  1,  1,  1,  1,  1,  1,  1,  0), \
+    DEFINE_REVISION(0x0800000A,  1,  2,  5,  1,  1,  1,  1,  1,  1,  1,  0), \
+    DEFINE_REVISION(0x0800000B,  1,  2,  5,  1,  2,  1,  1,  2,  1,  1,  0), \
+    DEFINE_REVISION(0x0800000C,  1,  2,  5,  1,  2,  1,  1,  2,  1,  1,  1)
 
 #endif  // _REVISION_H
diff --git a/include/stats_interface.h b/include/stats_interface.h
new file mode 100644 (file)
index 0000000..cb83a98
--- /dev/null
@@ -0,0 +1,186 @@
+/* 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
index cbe11ab6d9e4f4ad21ac54f0edbc6483728a577c..9970ff66b36e93a9362e6a0b7ccf205d4dc816bc 100644 (file)
@@ -152,12 +152,12 @@ DEFINE_GUID(GUID_XENBUS_SUSPEND_INTERFACE,
 */
 struct _XENBUS_SUSPEND_INTERFACE_V1 {
     INTERFACE                   Interface;
-    XENBUS_SUSPEND_ACQUIRE      Acquire;
-    XENBUS_SUSPEND_RELEASE      Release;
-    XENBUS_SUSPEND_REGISTER     Register;
-    XENBUS_SUSPEND_DEREGISTER   Deregister;
-    XENBUS_SUSPEND_TRIGGER      Trigger;
-    XENBUS_SUSPEND_GET_COUNT    GetCount;
+    XENBUS_SUSPEND_ACQUIRE      SuspendAcquire;
+    XENBUS_SUSPEND_RELEASE      SuspendRelease;
+    XENBUS_SUSPEND_REGISTER     SuspendRegister;
+    XENBUS_SUSPEND_DEREGISTER   SuspendDeregister;
+    XENBUS_SUSPEND_TRIGGER      SuspendTrigger;
+    XENBUS_SUSPEND_GET_COUNT    SuspendGetCount;
 };
 
 typedef struct _XENBUS_SUSPEND_INTERFACE_V1 XENBUS_SUSPEND_INTERFACE, *PXENBUS_SUSPEND_INTERFACE;
@@ -166,7 +166,7 @@ typedef struct _XENBUS_SUSPEND_INTERFACE_V1 XENBUS_SUSPEND_INTERFACE, *PXENBUS_S
     \brief Macro at assist in method invocation
 */
 #define XENBUS_SUSPEND(_Method, _Interface, ...)    \
-    (_Interface)-> ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+    (_Interface)->Suspend ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
 
 #endif  // _WINDLL
 
index ae365b73ee6274b3fa6734d0db5e66b290f99c86..f1ea9dffe9cdd81cb094202cddcd9e9776cf128f 100644 (file)
@@ -46,6 +46,7 @@
 #include <public/sched.h>
 #include <public/hvm/params.h>
 #include <public/hvm/hvm_info_table.h>
+#include <public/io/xen_stats.h>
 
 // xs_wire.h gates the definition of the xsd_errors enumeration
 // on whether EINVAL is defined. Unfortunately EINVAL is actually
diff --git a/include/xen/public/io/xen_stats.h b/include/xen/public/io/xen_stats.h
new file mode 100644 (file)
index 0000000..9451484
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef _XEN_STATS_H
+#define _XEN_STATS_H
+
+#define XEN_STATS_TYPE_INVALID  0
+#define XEN_STATS_TYPE_S64      1
+#define XEN_STATS_TYPE_U64      2
+#define XEN_STATS_TYPE_DOUBLE   3
+#define XEN_STATS_TYPE_ASCII    4
+
+struct xen_stats_name_type {
+    uint8_t type;
+    char    name[63];
+};
+
+struct xen_stats_value {
+    uint8_t buffer[64];
+};
+
+#endif  /* _XEN_STATS_H */
index 89d52d0adaf6efc605ea19bef71f5f0defbf8361..120ec947668b5fb00894f14680b0e7931483d816 100644 (file)
@@ -981,7 +981,7 @@ fail1:
     return FALSE;
 }
 
-#define DEFINE_REVISION(_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)
 
 static DWORD    DeviceRevision[] = {
index 92a3b19843709d7ccb0419a8e891f945bc8d9a05..00bb6a84a233409b79b207ae10650e2e06cef49c 100644 (file)
@@ -168,8 +168,8 @@ __FreePoolWithTag(
 }
 
 static FORCEINLINE PMDL
-__AllocatePage(
-    VOID
+__AllocatePages(
+    IN  ULONG           Count
     )
 {
     PHYSICAL_ADDRESS    LowAddress;
@@ -183,7 +183,7 @@ __AllocatePage(
     LowAddress.QuadPart = 0ull;
     HighAddress.QuadPart = ~0ull;
     SkipBytes.QuadPart = 0ull;
-    TotalBytes = (SIZE_T)PAGE_SIZE;
+    TotalBytes = (SIZE_T)PAGE_SIZE * Count;
 
     Mdl = MmAllocatePagesForMdlEx(LowAddress,
                                   HighAddress,
@@ -196,7 +196,7 @@ __AllocatePage(
     if (Mdl == NULL)
         goto fail1;
 
-    if (Mdl->ByteCount < PAGE_SIZE)
+    if (Mdl->ByteCount < TotalBytes)
         goto fail2;
 
     ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
@@ -219,7 +219,7 @@ __AllocatePage(
 
     ASSERT3P(MdlMappedSystemVa, ==, Mdl->MappedSystemVa);
 
-    RtlZeroMemory(MdlMappedSystemVa, PAGE_SIZE);
+    RtlZeroMemory(MdlMappedSystemVa, Mdl->ByteCount);
 
     return Mdl;
 
@@ -238,8 +238,10 @@ fail1:
     return NULL;
 }
 
+#define __AllocatePage()    __AllocatePages(1)
+
 static FORCEINLINE VOID
-__FreePage(
+__FreePages(
     IN PMDL    Mdl
     )
 {
@@ -248,13 +250,13 @@ __FreePage(
     ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
     MdlMappedSystemVa = Mdl->MappedSystemVa;
 
-    RtlFillMemory(MdlMappedSystemVa, PAGE_SIZE, 0xAA);
-
     MmUnmapLockedPages(MdlMappedSystemVa, Mdl);
 
     MmFreePagesFromMdl(Mdl);
 }
 
+#define __FreePage(_Mdl)    __FreePages(_Mdl)
+
 static FORCEINLINE PCHAR
 __strtok_r(
     IN      PCHAR   Buffer,
index f83cb1354f2f526f140e2bdba2f5f20127056176..e40622012dcbcc7c35772e1dc94d6407bb4970f4 100644 (file)
@@ -107,10 +107,11 @@ XenTouch(
     CHAR            Extra[XEN_EXTRAVERSION_LEN];
     NTSTATUS        status;
 
+    UNREFERENCED_PARAMETER(BuildNumber);
+
     if (MajorVersion != MAJOR_VERSION ||
         MinorVersion != MINOR_VERSION ||
-        MicroVersion != MICRO_VERSION ||
-        BuildNumber != BUILD_NUMBER)
+        MicroVersion != MICRO_VERSION)
         goto fail1;
 
     if (Reference++ != 0)
index 2104d54884edab003f8e4b989479e46c1b781796..e3fc74a44c467cd5e79d17440a9385f60f0ca801 100644 (file)
@@ -88,7 +88,7 @@ AddService=xenfilt,,XenFilt_Service,
 [XenBus_Service] 
 DisplayName=%XenBusName%
 ServiceType=%SERVICE_KERNEL_DRIVER% 
-StartType=%SERVICE_BOOT_START% 
+StartType=%SERVICE_DEMAND_START% 
 ErrorControl=%SERVICE_ERROR_NORMAL% 
 ServiceBinary=%12%\xenbus.sys 
 LoadOrderGroup="Boot Bus Extender"
index e3073f56260a16cd61bb69f2f34f67177e2b84bb..c6b5af0731aff495788bb764641964aa6fd04ccd 100644 (file)
@@ -818,7 +818,7 @@ loop:
         KeReleaseSpinLock(&Context->Lock, Irql);
     }
 
-    Trace("====>\n");
+    Trace("<====\n");
 
     return STATUS_SUCCESS;
 }
index ef31de1d83f76cb5e29efcb854acaed9d07fd0b3..133ef995f19623a2b64dc853bda49dd786090cb5 100644 (file)
@@ -59,6 +59,7 @@
 #include "driver.h"
 #include "range_set.h"
 #include "unplug.h"
+#include "stats.h"
 #include "dbg_print.h"
 #include "assert.h"
 #include "util.h"
@@ -127,6 +128,7 @@ struct _XENBUS_FDO {
     PXENBUS_GNTTAB_CONTEXT          GnttabContext;
     PXENBUS_UNPLUG_CONTEXT          UnplugContext;
     PXENBUS_BALLOON_CONTEXT         BalloonContext;
+    PXENBUS_STATS_CONTEXT           StatsContext;
 
     XENBUS_DEBUG_INTERFACE          DebugInterface;
     XENBUS_SUSPEND_INTERFACE        SuspendInterface;
@@ -788,6 +790,7 @@ DEFINE_FDO_GET_CONTEXT(Cache, PXENBUS_CACHE_CONTEXT)
 DEFINE_FDO_GET_CONTEXT(Gnttab, PXENBUS_GNTTAB_CONTEXT)
 DEFINE_FDO_GET_CONTEXT(Unplug, PXENBUS_UNPLUG_CONTEXT)
 DEFINE_FDO_GET_CONTEXT(Balloon, PXENBUS_BALLOON_CONTEXT)
+DEFINE_FDO_GET_CONTEXT(Stats, PXENBUS_STATS_CONTEXT)
 
 __drv_functionClass(IO_COMPLETION_ROUTINE)
 __drv_sameIRQL
@@ -5098,10 +5101,14 @@ FdoCreate(
     if (!NT_SUCCESS(status))
         goto fail17;
 
+    status = StatsInitialize(Fdo, &Fdo->StatsContext);
+    if (!NT_SUCCESS(status))
+        goto fail18;
+
     if (FdoIsBalloonEnabled(Fdo)) {
         status = BalloonInitialize(Fdo, &Fdo->BalloonContext);
         if (!NT_SUCCESS(status))
-            goto fail18;
+            goto fail19;
     }
 
     status = DebugGetInterface(__FdoGetDebugContext(Fdo),
@@ -5164,6 +5171,12 @@ done:
 
     return STATUS_SUCCESS;
 
+fail19:
+    Error("fail19\n");
+
+    StatsTeardown(Fdo->StatsContext);
+    Fdo->StatsContext = NULL;
+
 fail18:
     Error("fail18\n");
 
@@ -5327,6 +5340,9 @@ FdoDestroy(
             Fdo->BalloonContext = NULL;
         }
 
+        StatsTeardown(Fdo->StatsContext);
+        Fdo->StatsContext = NULL;
+
         UnplugTeardown(Fdo->UnplugContext);
         Fdo->UnplugContext = NULL;
 
index 9e9b5996a986530f6e2327e7c23f5f58c9985ed8..6351c34f370341a1aa85c32d175d8f374f2bc62b 100644 (file)
@@ -259,6 +259,13 @@ FdoGetUnplugContext(
     IN  PXENBUS_FDO Fdo
     );
 
+#include "stats.h"
+
+extern PXENBUS_STATS_CONTEXT
+FdoGetStatsContext(
+    IN  PXENBUS_FDO Fdo
+    );
+
 extern NTSTATUS
 FdoDispatch(
     IN  PXENBUS_FDO Fdo,
index 461ca7bbdd808f191c2f55df8ac2024e368847f9..96a608d904132eea2296949da4bbf2ea4458cb76 100644 (file)
@@ -364,10 +364,11 @@ typedef struct _XENBUS_PDO_REVISION {
     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
@@ -437,6 +438,13 @@ PdoDumpRevisions(
         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 -> "
@@ -449,7 +457,8 @@ PdoDumpRevisions(
              "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,
@@ -460,7 +469,8 @@ PdoDumpRevisions(
              Revision->CacheInterfaceVersion,
              Revision->GnttabInterfaceVersion,
              Revision->UnplugInterfaceVersion,
-             Revision->EmulatedInterfaceVersion);
+             Revision->EmulatedInterfaceVersion,
+             Revision->StatsInterfaceVersion);
     }
 }
 
@@ -1016,14 +1026,15 @@ done:                                                               \
     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 {
@@ -1034,14 +1045,15 @@ 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 }
diff --git a/src/xenbus/stats.c b/src/xenbus/stats.c
new file mode 100644 (file)
index 0000000..7fcbd78
--- /dev/null
@@ -0,0 +1,1172 @@
+/* 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");
+}
diff --git a/src/xenbus/stats.h b/src/xenbus/stats.h
new file mode 100644 (file)
index 0000000..f963b92
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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_STATS_H
+#define _XENBUS_STATS_H
+
+#include <ntddk.h>
+#include <xen.h>
+#include <stats_interface.h>
+
+typedef struct _XENBUS_STATS_CONTEXT  XENBUS_STATS_CONTEXT, *PXENBUS_STATS_CONTEXT;
+
+#include "fdo.h"
+
+extern NTSTATUS
+StatsInitialize(
+    IN  PXENBUS_FDO             Fdo,
+    OUT PXENBUS_STATS_CONTEXT   *Context
+    );
+
+extern NTSTATUS
+StatsGetInterface(
+    IN      PXENBUS_STATS_CONTEXT   Context,
+    IN      ULONG                   Version,
+    IN OUT  PINTERFACE              Interface,
+    IN      ULONG                   Size
+    );
+
+extern ULONG
+StatsGetReferences(
+    IN  PXENBUS_STATS_CONTEXT   Context
+    );
+
+extern VOID
+StatsTeardown(
+    IN  PXENBUS_STATS_CONTEXT   Context
+    );
+
+#endif  // _XENBUS_STATS_H
index 2cffb9f9bbeaa81d628cf0bf1ef01efc328e067f..980d54d89e910a4505e7c5d5d5c36e322523eef6 100644 (file)
@@ -79,6 +79,7 @@
     <ClCompile Include="..\..\src\xenbus\gnttab.c" />
     <ClCompile Include="..\..\src\xenbus\pdo.c" />
     <ClCompile Include="..\..\src\xenbus\shared_info.c" />
+    <ClCompile Include="..\..\src\xenbus\stats.c" />
     <ClCompile Include="..\..\src\xenbus\store.c" />
     <ClCompile Include="..\..\src\xenbus\suspend.c" />
     <ClCompile Include="..\..\src\xenbus\sync.c" />
index 396245f8ae377ebde967825dc1a4851c06f186af..51bd703752b937f611ae940a3d49684e4262fb99 100644 (file)
@@ -82,6 +82,7 @@
     <ClCompile Include="..\..\src\xenbus\gnttab.c" />
     <ClCompile Include="..\..\src\xenbus\pdo.c" />
     <ClCompile Include="..\..\src\xenbus\shared_info.c" />
+    <ClCompile Include="..\..\src\xenbus\stats.c" />
     <ClCompile Include="..\..\src\xenbus\store.c" />
     <ClCompile Include="..\..\src\xenbus\suspend.c" />
     <ClCompile Include="..\..\src\xenbus\sync.c" />