[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [RFC PATCH] Use a FreeList for CACHE entries


  • To: Owen Smith <owen.smith@xxxxxxxxxx>, "win-pv-devel@xxxxxxxxxxxxxxxxxxxx" <win-pv-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Owen Smith <owen.smith@xxxxxxxxxx>
  • Date: Thu, 28 Jul 2022 14:54:50 +0000
  • Accept-language: en-GB, en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=MLCDG8Iam1GIE4y0UHgxmfb68U1/nev1jgLv4WCBmog=; b=MulX7g7l8uZ2/R+fZovA2Vawl1zMj4e5tG20jF9Vy3qF0r0gyAugT8z4RCiTLDJr35EX9lm3u4IzWJgK55bJbJWK1uJnWKx0i1Q9i3ajcaGrFoyXG6UkA3SUAs9yeaC3kXgqaBRG0vVSHz8kwpa/BvG3/RWZfGI4vs27vNnsUv0o+vp3wUtGoA3sZk/GOumcg/n0p62BG1B8OYmTqgvjwu+uPlPxehL6Fd2YzeOBdHUapWKQJBqi8VtYSS3wvnkXh5D03cf2x1+Z924fsUFv/yUx4tmTtX331f7uWcmy3ldjx+nLdX90baiUi8PighnPUFKdE2+2tN8lfsqEK/p/bw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=cqfzWZGdmSI2HLP4j37oa8bo/GPCerxxjw2PUsZS8pd7AC1aR6QeP5n5dQbvq1olrSqv6918v56u5GMuMyBXoRC2MIp2cW6+VazrQJjNIsjVotBOTOc+vY90bbj+0eT5pT8ehQ1vyLEcHaeceaZkK+u8lcBXKi2umXXG8d0ZnUn7AzPUdqIWpB0zHGZBrUVKwzS+yWZOOaueyKH/qk1DUPWspigSjbikQVA+tiMyFq8qJHznvcfGOdDo5hdnE55OQLLWnRS8XuYde3/6SOVS2J364eOqb2igzjKeV90uUAP3q1MrRo9+utlCsdFRM0is8LbRqlaLcAh5tjBQCXVF5A==
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Delivery-date: Thu, 28 Jul 2022 14:55:09 +0000
  • Ironport-data: A9a23:984Xtq2HtzTmWTemDvbD5a1wkn2cJEfYwER7XKvMYLTBsI5bpzIPy WAYCz3Ub/jcY2KmfIwiPY+1oEsF7MXdm9IwGVY4pC1hF35El5HIVI+TRqvS04J+DSFhoGZPt Zh2hgzodZhsJpPkjk7xdOKn9RGQ7InQLpLkEunIJyttcgFtTSYlmHpLlvUwx4VlmrBVOSvU0 T/Ji5CZaQTNNwJcaDpOsfrc8kM35pwehRtD1rAATaET1LPhvyF94KI3fcmZM3b+S49IKe+2L 86rIGaRpz6xE78FU7tJo56jGqE4aue60Tum0xK6b5OKkBlazhHe545gXBYqheW7vB3S9zx54 I0lWZVd0m7FNIWU8AgWe0Ew/y2TocSqUVIISJSymZX78qHIT5fj6800PkEYPpUAxrhcOUJA2 KJBGgtTUynW0opawJrjIgVtruIKCZCxeaYg4DRnxzyfCus6S5feRamM/cVfwDo7msFJG7DZe tYdbj1sKh/HZnWjOH9OUM54wLju2Ce5L2cwRFG9/MLb50DU0wF3lqPoMcbVUteLWd9UjgCTo WeuE2HRXU9EaIXOl2TtHnSElsSRzHijXZopC6Ti0f1UkkDP60k4MUhDPbe8ibzj4qKkYPpAK kpR4jRroaUs+UiDStjmQwb+sHOCpgQbWddbD6s98g7l90bPywOQB2xBSyEbbtUj7ZUyXWZyi gXPmM71DztytrHTUWia6rqfsTK1P24SMHMGYigHCwAC5rEPvb0Os/4Gdf47eIbdszE/MWiYL +yixMTmu4gusA==
  • Ironport-hdrordr: A9a23:3TC0A6k9cjQQodzuCJ3g+E0FtCbpDfOSimdD5ihNYBxZY6Wkfp +V8cjzhCWftN9OYhodcIi7SdG9qADnhOVICO4qTPyftWjdySOVxeRZgbcKrAeQfxEWmtQ96U 4kSdkGNDSSNykxsS+Z2njeLz9I+rDun86VbKXlvhFQpGpRGsJdBnJCe2Om+zpNNWt77PQCdK a0145inX6NaH4XZsO0Cj0uRO7YveDGk5rgfFovGwMnwBPmt0Ll1JfKVzyjmjsOWTJGxrkvtU LflRbi26mlu/anjjfBym7o6YhMkteJ8KoNOCXMsLlaFtzfsHfpWG1TYczAgNnzmpDs1L8eqq iMn/7nBbU315qeRBDwnfKn4Xib7N9n0Q6e9bbfuwqvnSWxfkNHN+NRwY1eaRfX8EwmoZV117 9KxXuQs95NAQrHhzmV3am+a/hGrDvAnZMZq59ms1VPFY8FLLNBp40W+01YVJ8GASLh8YgiVO 1jFtvV6vpaeU6TKymxhBgn/PW8GnAoWhuWSEkLvcKYlzBQgXBi1kMdgMgShG0J+p4xQ4RNo+ 7ELqNrnrdTSdJ+V9MKOM4RBc+sTmDdSxPFN2yfZVzhCaEcInrI74X65b0kjdvaCqDgDKFC66 gpfGkoy1LaIXiedvFm9Kc7gyzlUSG6QSnnzN1Y6txwpqD8LYCbQRG+dA==
  • List-id: Developer list for the Windows PV Drivers subproject <win-pv-devel.lists.xenproject.org>
  • Thread-index: AQHYopHZ+EEIoV9zO0i2uJdxKcvJ3K2T3pdQ
  • Thread-topic: [RFC PATCH] Use a FreeList for CACHE entries

RFC patch - there are still outstanding bugs that need addressing, but I wanted 
to get comments on the issue and proposed fix

Owen

-----Original Message-----
From: win-pv-devel <win-pv-devel-bounces@xxxxxxxxxxxxxxxxxxxx> On Behalf Of 
Owen Smith
Sent: 28 July 2022 15:53
To: win-pv-devel@xxxxxxxxxxxxxxxxxxxx
Cc: Owen Smith <owen.smith@xxxxxxxxxx>
Subject: [RFC PATCH] Use a FreeList for CACHE entries

[CAUTION - EXTERNAL EMAIL] DO NOT reply, click links, or open attachments 
unless you have verified the sender and know the content is safe.

The CACHE slabs can reserve a large number of resources, particularly when the 
object constructor allocates memory or reserves grant table entries, which 
leads to resource starvation far quicker than expected.

For Example, the GNTTAB object constructor will reserve a grant entry, while 
the GNTTAB cache will assign 253 objects per slab (i.e. a slab will refer to
253 grantref_t's). The XenVif receiver ring will require 257 GNTTAB objects, so 
2 slabs will be allocated (meaning 506 grantref_t's are reserved per receive 
queue, while only 257 will ever be used).

Add CACHE v3 that allows callers to specify how many objects are created in a 
single pass (default 1) which will allow CACHE objects to grow at a more 
managable pace, while maintaining overhead for bursty operations to limit the 
number of the slow-path allocation cycles.

Note: RFC patch, there are issues that still need investigating

Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx>
---
 include/cache_interface.h |  75 ++++-
 include/revision.h        |   3 +-
 src/xenbus/cache.c        | 608 ++++++++++++--------------------------
 src/xenbus/gnttab.c       |   1 +
 4 files changed, 257 insertions(+), 430 deletions(-)

diff --git a/include/cache_interface.h b/include/cache_interface.h index 
ce50f4e..839212c 100644
--- a/include/cache_interface.h
+++ b/include/cache_interface.h
@@ -123,6 +123,23 @@ typedef VOID
     IN  PVOID   Argument
     );
 
+/*! \typedef XENBUS_CACHE_CREATE_V1
+    \brief Create a cache of objects of the given \a Size
+
+    \param Interface The interface header
+    \param Name A name for the cache which will be used in debug output
+    \param Size The size of each object in bytes
+    \param Reservation The target minimum population of the cache
+    \param Ctor A callback which is invoked when a new object created
+    \param Dtor A callback which is invoked when an object is destroyed
+    \param AcquireLock A callback invoked to acquire a spinlock
+    \param ReleaseLock A callback invoked to release the spinlock
+    \param Argument An optional context argument passed to the callbacks
+    \param Cache A pointer to a cache handle to be initialized
+
+    If a non-zero \a Reservation is specified then this method will fail
+    unless that number of objects can be immediately created.
+*/
 typedef NTSTATUS
 (*XENBUS_CACHE_CREATE_V1)(
     IN  PINTERFACE                  Interface,
@@ -137,6 +154,39 @@ typedef NTSTATUS
     OUT PXENBUS_CACHE               *Cache
     );
 
+/*! \typedef XENBUS_CACHE_CREATE_V2
+    \brief Create a cache of objects of the given \a Size
+
+    \param Interface The interface header
+    \param Name A name for the cache which will be used in debug output
+    \param Size The size of each object in bytes
+    \param Reservation The target minimum population of the cache
+    \param Cap The maximum population of the cache
+    \param Ctor A callback which is invoked when a new object created
+    \param Dtor A callback which is invoked when an object is destroyed
+    \param AcquireLock A callback invoked to acquire a spinlock
+    \param ReleaseLock A callback invoked to release the spinlock
+    \param Argument An optional context argument passed to the callbacks
+    \param Cache A pointer to a cache handle to be initialized
+
+    If a non-zero \a Reservation is specified then this method will fail
+    unless that number of objects can be immediately created.
+*/
+typedef NTSTATUS
+(*XENBUS_CACHE_CREATE_V2)(
+    IN  PINTERFACE                  Interface,
+    IN  const CHAR                  *Name,
+    IN  ULONG                       Size,
+    IN  ULONG                       Reservation,
+    IN  ULONG                       Cap,
+    IN  XENBUS_CACHE_CTOR           Ctor,
+    IN  XENBUS_CACHE_DTOR           Dtor,
+    IN  XENBUS_CACHE_ACQUIRE_LOCK   AcquireLock,
+    IN  XENBUS_CACHE_RELEASE_LOCK   ReleaseLock,
+    IN  PVOID                       Argument OPTIONAL,
+    OUT PXENBUS_CACHE               *Cache
+    );
+
 /*! \typedef XENBUS_CACHE_CREATE
     \brief Create a cache of objects of the given \a Size
 
@@ -145,6 +195,7 @@ typedef NTSTATUS
     \param Size The size of each object in bytes
     \param Reservation The target minimum population of the cache
     \param Cap The maximum population of the cache
+    \param Stride The number of objects to initialize in one go.
     \param Ctor A callback which is invoked when a new object created
     \param Dtor A callback which is invoked when an object is destroyed
     \param AcquireLock A callback invoked to acquire a spinlock @@ -154,7 
+205,7 @@ typedef NTSTATUS
 
     If a non-zero \a Reservation is specified then this method will fail
     unless that number of objects can be immediately created.
-*/  
+*/
 typedef NTSTATUS
 (*XENBUS_CACHE_CREATE)(
     IN  PINTERFACE                  Interface,
@@ -162,6 +213,7 @@ typedef NTSTATUS
     IN  ULONG                       Size,
     IN  ULONG                       Reservation,
     IN  ULONG                       Cap,
+    IN  ULONG                       Stride,
     IN  XENBUS_CACHE_CTOR           Ctor,
     IN  XENBUS_CACHE_DTOR           Dtor,
     IN  XENBUS_CACHE_ACQUIRE_LOCK   AcquireLock,
@@ -234,20 +286,33 @@ struct _XENBUS_CACHE_INTERFACE_V1 {  };
 
 /*! \struct _XENBUS_CACHE_INTERFACE_V2
-    \brief CACHE interface version 1
+    \brief CACHE interface version 2
     \ingroup interfaces
 */
 struct _XENBUS_CACHE_INTERFACE_V2 {
     INTERFACE               Interface;
     XENBUS_CACHE_ACQUIRE    CacheAcquire;
     XENBUS_CACHE_RELEASE    CacheRelease;
-    XENBUS_CACHE_CREATE     CacheCreate;
+    XENBUS_CACHE_CREATE_V2  CacheCreate;
     XENBUS_CACHE_GET        CacheGet;
     XENBUS_CACHE_PUT        CachePut;
     XENBUS_CACHE_DESTROY    CacheDestroy;
 };
 
-typedef struct _XENBUS_CACHE_INTERFACE_V2 XENBUS_CACHE_INTERFACE, 
*PXENBUS_CACHE_INTERFACE;
+/*! \struct _XENBUS_CACHE_INTERFACE_V3
+    \brief CACHE interface version 3
+    \ingroup interfaces
+*/
+struct _XENBUS_CACHE_INTERFACE_V3 {
+    INTERFACE               Interface;
+    XENBUS_CACHE_ACQUIRE    CacheAcquire;
+    XENBUS_CACHE_RELEASE    CacheRelease;
+    XENBUS_CACHE_CREATE     CacheCreate;
+    XENBUS_CACHE_GET        CacheGet;
+    XENBUS_CACHE_PUT        CachePut;
+    XENBUS_CACHE_DESTROY    CacheDestroy;
+};
+typedef struct _XENBUS_CACHE_INTERFACE_V3 XENBUS_CACHE_INTERFACE, 
+*PXENBUS_CACHE_INTERFACE;
 
 /*! \def XENBUS_CACHE
     \brief Macro at assist in method invocation @@ -258,6 +323,6 @@ typedef 
struct _XENBUS_CACHE_INTERFACE_V2 XENBUS_CACHE_INTERFACE, *PXENBUS_CACHE  
#endif  // _WINDLL
 
 #define XENBUS_CACHE_INTERFACE_VERSION_MIN  1 -#define 
XENBUS_CACHE_INTERFACE_VERSION_MAX  2
+#define XENBUS_CACHE_INTERFACE_VERSION_MAX  3
 
 #endif  // _XENBUS_CACHE_INTERFACE_H
diff --git a/include/revision.h b/include/revision.h index 3e3779f..e3fd789 
100644
--- a/include/revision.h
+++ b/include/revision.h
@@ -56,6 +56,7 @@
     DEFINE_REVISION(0x09000006,  1,  3,  8,  1,  2,  1,  2,  4,  1,  1,  1), \
     DEFINE_REVISION(0x09000007,  1,  3,  8,  1,  2,  1,  2,  4,  1,  1,  2), \
     DEFINE_REVISION(0x09000008,  1,  3,  9,  1,  2,  1,  2,  4,  1,  1,  2), \
-    DEFINE_REVISION(0x09000009,  1,  4,  9,  1,  2,  1,  2,  4,  1,  1,  2)
+    DEFINE_REVISION(0x09000009,  1,  4,  9,  1,  2,  1,  2,  4,  1,  1,  2), \
+    DEFINE_REVISION(0x0900000A,  1,  4,  9,  1,  2,  1,  3,  4,  1,  1,  
+ 2)
 
 #endif  // _REVISION_H
diff --git a/src/xenbus/cache.c b/src/xenbus/cache.c index 13fb0e5..4f47459 
100644
--- a/src/xenbus/cache.c
+++ b/src/xenbus/cache.c
@@ -55,15 +55,12 @@ typedef struct _XENBUS_CACHE_MAGAZINE {
 
 #define XENBUS_CACHE_SLAB_MAGIC 'BALS'
 
-typedef struct _XENBUS_CACHE_SLAB {
+typedef struct _XENBUS_CACHE_ENTRY {
     ULONG           Magic;
     PXENBUS_CACHE   Cache;
     LIST_ENTRY      ListEntry;
-    USHORT          MaximumOccupancy;
-    USHORT          CurrentOccupancy;
-    ULONG           *Mask;
     UCHAR           Buffer[1];
-} XENBUS_CACHE_SLAB, *PXENBUS_CACHE_SLAB;
+} XENBUS_CACHE_ENTRY, *PXENBUS_CACHE_ENTRY;
 
 #define BITS_PER_ULONG  (sizeof (ULONG) * 8)
 #define MAXNAMELEN      128
@@ -74,20 +71,19 @@ struct _XENBUS_CACHE {
     ULONG                   Size;
     ULONG                   Reservation;
     ULONG                   Cap;
+    ULONG                   Stride;
     NTSTATUS                (*Ctor)(PVOID, PVOID);
     VOID                    (*Dtor)(PVOID, PVOID);
     VOID                    (*AcquireLock)(PVOID);
     VOID                    (*ReleaseLock)(PVOID);
     PVOID                   Argument;
-    LIST_ENTRY              SlabList;
-    PLIST_ENTRY             Cursor;
-    ULONG                   Count;
     PXENBUS_CACHE_MAGAZINE  Magazine;
     ULONG                   MagazineCount;
-    LONG                    CurrentSlabs;
-    LONG                    MaximumSlabs;
+    LONG                    FreeSize;
+    LONG                    FreeMaximum;
     LONG                    CurrentObjects;
     LONG                    MaximumObjects;
+    LIST_ENTRY              FreeList;
 };
 
 struct _XENBUS_CACHE_CONTEXT {
@@ -195,333 +191,103 @@ CachePutObjectToMagazine(
     return STATUS_UNSUCCESSFUL;
 }
 
-static VOID
-CacheInsertSlab(
-    IN  PXENBUS_CACHE       Cache,
-    IN  PXENBUS_CACHE_SLAB  New
-    )
-{
-#define INSERT_BEFORE(_ListEntry, _New)             \
-        do {                                        \
-            (_New)->Blink = (_ListEntry)->Blink;    \
-            (_ListEntry)->Blink->Flink = (_New);    \
-                                                    \
-            (_ListEntry)->Blink = (_New);           \
-            (_New)->Flink = (_ListEntry);           \
-        } while (FALSE)
-
-    PLIST_ENTRY             ListEntry;
-
-    ASSERT(New->CurrentOccupancy < New->MaximumOccupancy);
-
-    Cache->Cursor = NULL;
-
-    for (ListEntry = Cache->SlabList.Flink;
-         ListEntry != &Cache->SlabList;
-         ListEntry = ListEntry->Flink) {
-        PXENBUS_CACHE_SLAB  Slab;
-
-        Slab = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_SLAB, ListEntry);
-
-        if (Slab->CurrentOccupancy < New->CurrentOccupancy) {
-            INSERT_BEFORE(ListEntry, &New->ListEntry);
-            goto done;
-        }
-
-        if (Slab->CurrentOccupancy < Slab->MaximumOccupancy &&
-            Cache->Cursor == NULL)
-            Cache->Cursor = ListEntry;
-    }
-
-    InsertTailList(&Cache->SlabList, &New->ListEntry);
-
-done:
-    if (Cache->Cursor == NULL)
-        Cache->Cursor = &New->ListEntry;
-
-#undef  INSERT_BEFORE
-}
-
-#if DBG
-static VOID
-CacheAudit(
-    IN  PXENBUS_CACHE   Cache
-    )
-{
-    ULONG               CurrentOccupancy = ULONG_MAX;
-    PLIST_ENTRY         ListEntry;
-
-    //
-    // The cursror should point at the first slab that is not fully
-    // occupied.
-    //
-    for (ListEntry = Cache->SlabList.Flink;
-         ListEntry != &Cache->SlabList;
-         ListEntry = ListEntry->Flink) {
-        PXENBUS_CACHE_SLAB  Slab;
-
-        Slab = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_SLAB, ListEntry);
-
-        if (Slab->CurrentOccupancy < Slab->MaximumOccupancy) {
-            ASSERT3P(Cache->Cursor, ==, ListEntry);
-            break;
-        }
-    }
-
-    // Slabs should be kept in order of maximum to minimum occupancy
-    for (ListEntry = Cache->SlabList.Flink;
-         ListEntry != &Cache->SlabList;
-         ListEntry = ListEntry->Flink) {
-        PXENBUS_CACHE_SLAB  Slab;
-
-        Slab = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_SLAB, ListEntry);
-
-        ASSERT3U(Slab->CurrentOccupancy, <=, CurrentOccupancy);
-
-        CurrentOccupancy = Slab->CurrentOccupancy;
-    }
-}
-#else
-#define CacheAudit(_Cache) ((VOID)(_Cache)) -#endif
-
-// Must be called with lock held
 static NTSTATUS
-CacheCreateSlab(
-    IN  PXENBUS_CACHE   Cache
+CacheFill(
+    IN  PXENBUS_CACHE   Cache,
+    IN  ULONG           Count,
+    IN  BOOLEAN         Locked
     )
 {
-    PXENBUS_CACHE_SLAB  Slab;
-    ULONG               NumberOfBytes;
-    ULONG               Count;
+    KIRQL               Irql;
+    PXENBUS_CACHE_ENTRY Entry;
     ULONG               Size;
-    LONG                Index;
-    LONG                SlabCount;
     NTSTATUS            status;
 
-    NumberOfBytes = P2ROUNDUP(FIELD_OFFSET(XENBUS_CACHE_SLAB, Buffer) +
-                              Cache->Size,
-                              PAGE_SIZE);
-    Count = (NumberOfBytes - FIELD_OFFSET(XENBUS_CACHE_SLAB, Buffer)) /
-            Cache->Size;
-    ASSERT(Count != 0);
-
-    status = STATUS_INSUFFICIENT_RESOURCES;
-    if (Cache->Count + Count > Cache->Cap)
-        goto fail1;
-
-    Slab = __CacheAllocate(NumberOfBytes);
-    ASSERT3P(Slab, ==, PAGE_ALIGN(Slab));
-
-    status = STATUS_NO_MEMORY;
-    if (Slab == NULL)
-        goto fail2;
-
-    RtlZeroMemory(Slab, NumberOfBytes);
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    if (!Locked)
+        __CacheAcquireLock(Cache);
 
-    Slab->Magic = XENBUS_CACHE_SLAB_MAGIC;
-    Slab->Cache = Cache;
-    Slab->MaximumOccupancy = (USHORT)Count;
+    Size = P2ROUNDUP(FIELD_OFFSET(XENBUS_CACHE_ENTRY, Buffer) +
+                     Cache->Size,
+                     sizeof(ULONG_PTR));
 
-    Size = P2ROUNDUP(Count, BITS_PER_ULONG);
-    Size /= 8;
+    status = STATUS_SUCCESS;
+    while (Cache->FreeSize < (LONG)Count) {
+        LONG                ObjectCount;
 
-    Slab->Mask = __CacheAllocate(Size);
-    if (Slab->Mask == NULL)
-        goto fail3;
+        status = STATUS_NO_MEMORY;
+        Entry = __CacheAllocate(Size);
+        if (Entry == NULL)
+            goto fail1;
 
-    for (Index = 0; Index < (LONG)Slab->MaximumOccupancy; Index++) {
-        PVOID Object = (PVOID)&Slab->Buffer[Index * Cache->Size];
+        Entry->Magic = XENBUS_CACHE_SLAB_MAGIC;
+        Entry->Cache = Cache;
+        InitializeListHead(&Entry->ListEntry);
 
-        status = __CacheCtor(Cache, Object);
+        status = __CacheCtor(Cache, Entry->Buffer);
         if (!NT_SUCCESS(status))
-            goto fail4;
-    }
-
-    CacheInsertSlab(Cache, Slab);
-    Cache->Count += Count;
-
-    SlabCount = InterlockedIncrement(&Cache->CurrentSlabs);
-    if (SlabCount > Cache->MaximumSlabs)
-        Cache->MaximumSlabs = SlabCount;
-
-    return STATUS_SUCCESS;
-
-fail4:
-    Error("fail4\n");
+            goto fail2;
 
-    while (--Index >= 0) {
-        PVOID Object = (PVOID)&Slab->Buffer[Index * Cache->Size];
+        InsertTailList(&Cache->FreeList, &Entry->ListEntry);
+        ObjectCount = InterlockedIncrement(&Cache->FreeSize);
 
-        __CacheDtor(Cache, Object);
+        if (ObjectCount > Cache->FreeMaximum)
+            Cache->FreeMaximum = ObjectCount;
     }
 
-    __CacheFree(Slab->Mask);
+    if (!Locked)
+        __CacheReleaseLock(Cache);
 
-fail3:
-    Error("fail3\n");
+    KeLowerIrql(Irql);
 
-    __CacheFree(Slab);
+    return STATUS_SUCCESS;
 
 fail2:
-    Error("fail2\n");
+    __CacheFree(Entry);
 
 fail1:
-    Error("fail1 (%08x)\n", status);
+    if (!Locked)
+        __CacheReleaseLock(Cache);
+
+    KeLowerIrql(Irql);
 
     return status;
 }
 
-// Must be called with lock held
 static VOID
-CacheDestroySlab(
-    IN  PXENBUS_CACHE       Cache,
-    IN  PXENBUS_CACHE_SLAB  Slab
+CacheSpill(
+    IN  PXENBUS_CACHE   Cache,
+    IN  ULONG           Count,
+    IN  BOOLEAN         Locked
     )
 {
-    LONG                    Index;
-
-    ASSERT3U(Slab->CurrentOccupancy, ==, 0);
-
-    ASSERT3U(Cache->Count, >=, Slab->MaximumOccupancy);
-    Cache->Count -= Slab->MaximumOccupancy;
-
-    //
-    // The only reason the cursor should be pointing at this slab is
-    // if it is the only one in the list.
-    //
-    if (Cache->Cursor == &Slab->ListEntry)
-        Cache->Cursor = &Cache->SlabList;
-
-    RemoveEntryList(&Slab->ListEntry);
-
-    ASSERT(Cache->Cursor != &Cache->SlabList ||
-           IsListEmpty(&Cache->SlabList));
-
-    Index = Slab->MaximumOccupancy;
-    while (--Index >= 0) {
-        PVOID Object = (PVOID)&Slab->Buffer[Index * Cache->Size];
-
-        __CacheDtor(Cache, Object);
-    }
+    KIRQL               Irql;
+    PLIST_ENTRY         ListEntry;
 
-    ASSERT(Cache->CurrentSlabs != 0);
-    InterlockedDecrement(&Cache->CurrentSlabs);
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    if (!Locked)
+        __CacheAcquireLock(Cache);
 
-    __CacheFree(Slab->Mask);
-    __CacheFree(Slab);
-}
+    while (!IsListEmpty(&Cache->FreeList)) {
+        PXENBUS_CACHE_ENTRY Entry;
 
-static FORCEINLINE ULONG
-__CacheMaskScan(
-    IN  ULONG   *Mask,
-    IN  ULONG   Maximum
-    )
-{
-    ULONG       Size;
-    ULONG       Index;
-
-    Size = P2ROUNDUP(Maximum, BITS_PER_ULONG);
-    Size /= sizeof (ULONG);
-    ASSERT(Size != 0);
+        if (Cache->FreeSize <= (LONG)Count)
+            break;
 
-    for (Index = 0; Index < Size; Index++) {
-        ULONG   Free = ~Mask[Index];
-        ULONG   Bit;
+        ListEntry = RemoveTailList(&Cache->FreeList);
+        ASSERT3P(ListEntry, !=, &Cache->FreeList);
+        InterlockedDecrement(&Cache->FreeSize);
 
-        if (!_BitScanForward(&Bit, Free))
-            continue;
+        Entry = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_ENTRY, 
+ ListEntry);
 
-        Bit += Index * BITS_PER_ULONG;
-        if (Bit < Maximum)
-            return Bit;
+        __CacheDtor(Cache, Entry->Buffer);
+        __CacheFree(Entry);
     }
 
-    return Maximum;
-}
-
-static FORCEINLINE VOID
-__CacheMaskSet(
-    IN  ULONG   *Mask,
-    IN  ULONG   Bit
-    )
-{
-    ULONG       Index = Bit / BITS_PER_ULONG;
-
-    Mask[Index] |= 1u << (Bit % BITS_PER_ULONG);
-}
-
-static FORCEINLINE BOOLEAN
-__CacheMaskTest(
-    IN  ULONG   *Mask,
-    IN  ULONG   Bit
-    )
-{
-    ULONG       Index = Bit / BITS_PER_ULONG;
-
-    return (Mask[Index] & (1u << (Bit % BITS_PER_ULONG))) ? TRUE : FALSE;
-}
-
-static FORCEINLINE VOID
-__CacheMaskClear(
-    IN  ULONG   *Mask,
-    IN  ULONG   Bit
-    )
-{
-    ULONG       Index = Bit / BITS_PER_ULONG;
-
-    Mask[Index] &= ~(1u << (Bit % BITS_PER_ULONG));
-}
-
-// Must be called with lock held
-static PVOID
-CacheGetObjectFromSlab(
-    IN  PXENBUS_CACHE_SLAB  Slab
-    )
-{
-    PXENBUS_CACHE           Cache;
-    ULONG                   Index;
-    PVOID                   Object;
-
-    Cache = Slab->Cache;
-
-    ASSERT3U(Slab->CurrentOccupancy, <=, Slab->MaximumOccupancy);
-    if (Slab->CurrentOccupancy == Slab->MaximumOccupancy)
-        return NULL;
-
-    Index = __CacheMaskScan(Slab->Mask, Slab->MaximumOccupancy);
-    BUG_ON(Index >= Slab->MaximumOccupancy);
-
-    __CacheMaskSet(Slab->Mask, Index);
-    Slab->CurrentOccupancy++;
-
-    Object = (PVOID)&Slab->Buffer[Index * Cache->Size];
-    ASSERT3U(Index, ==, (ULONG)((PUCHAR)Object - &Slab->Buffer[0]) /
-             Cache->Size);
-
-    return Object;
-}
-
-// Must be called with lock held
-static VOID
-CachePutObjectToSlab(
-    IN  PXENBUS_CACHE_SLAB  Slab,
-    IN  PVOID               Object
-    )
-{
-    PXENBUS_CACHE           Cache;
-    ULONG                   Index;
-
-    Cache = Slab->Cache;
-
-    Index = (ULONG)((PUCHAR)Object - &Slab->Buffer[0]) / Cache->Size;
-    BUG_ON(Index >= Slab->MaximumOccupancy);
-
-    ASSERT(Slab->CurrentOccupancy != 0);
-    --Slab->CurrentOccupancy;
+    if (!Locked)
+        __CacheReleaseLock(Cache);
 
-    ASSERT(__CacheMaskTest(Slab->Mask, Index));
-    __CacheMaskClear(Slab->Mask, Index);
+    KeLowerIrql(Irql);
 }
 
 static PVOID
@@ -552,34 +318,22 @@ CacheGet(
     if (!Locked)
         __CacheAcquireLock(Cache);
 
-again:
-    if (Cache->Cursor != &Cache->SlabList) {
-        PLIST_ENTRY ListEntry = Cache->Cursor;
-        PXENBUS_CACHE_SLAB  Slab;
-
-        Slab = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_SLAB, ListEntry);
-
-        Object = CacheGetObjectFromSlab(Slab);
-        ASSERT(Object != NULL);
-
-        if (Slab->CurrentOccupancy == Slab->MaximumOccupancy)
-            Cache->Cursor = Slab->ListEntry.Flink;
+    if (IsListEmpty(&Cache->FreeList)) {
+        (VOID) CacheFill(Cache, Cache->Stride, TRUE);
     }
 
-    if (Object == NULL) {
-        NTSTATUS status;
+    if (!IsListEmpty(&Cache->FreeList)) {
+        PLIST_ENTRY         ListEntry;
+        PXENBUS_CACHE_ENTRY Entry;
 
-        ASSERT3P(Cache->Cursor, ==, &Cache->SlabList);
+        ListEntry = RemoveHeadList(&Cache->FreeList);
+        ASSERT3P(ListEntry, !=, &Cache->FreeList);
+        InterlockedDecrement(&Cache->FreeSize);
 
-        status = CacheCreateSlab(Cache);
-        if (NT_SUCCESS(status)) {
-            ASSERT(Cache->Cursor != &Cache->SlabList);
-            goto again;
-        }
+        Entry = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_ENTRY, ListEntry);
+        Object = Entry->Buffer;
     }
 
-    CacheAudit(Cache);
-
     if (!Locked)
         __CacheReleaseLock(Cache);
 
@@ -606,7 +360,8 @@ CachePut(
     KIRQL                   Irql;
     ULONG                   Index;
     PXENBUS_CACHE_MAGAZINE  Magazine;
-    PXENBUS_CACHE_SLAB      Slab;
+    PXENBUS_CACHE_ENTRY     Entry;
+    LONG                    ObjectCount;
     NTSTATUS                status;
 
     UNREFERENCED_PARAMETER(Interface);
@@ -622,19 +377,22 @@ CachePut(
     if (NT_SUCCESS(status))
         goto done;
 
-    Slab = (PXENBUS_CACHE_SLAB)PAGE_ALIGN(Object);
-    ASSERT3U(Slab->Magic, ==, XENBUS_CACHE_SLAB_MAGIC);
+    Entry = CONTAINING_RECORD(Object, XENBUS_CACHE_ENTRY, Buffer);
+    ASSERT3U(Entry->Magic, ==, XENBUS_CACHE_SLAB_MAGIC);
 
     if (!Locked)
         __CacheAcquireLock(Cache);
 
-    CachePutObjectToSlab(Slab, Object);
+    InsertHeadList(&Cache->FreeList, &Entry->ListEntry);
+    ObjectCount = InterlockedIncrement(&Cache->FreeSize);
+    if (ObjectCount > Cache->FreeMaximum)
+        Cache->FreeMaximum = ObjectCount;
 
-    /* Re-insert to keep slab list ordered */
-    RemoveEntryList(&Slab->ListEntry);
-    CacheInsertSlab(Cache, Slab);
-
-    CacheAudit(Cache);
+    if (Cache->FreeSize > (LONG)(Cache->Reservation + Cache->Stride))
+        CacheSpill(Cache,
+                   __max((Cache->Reservation + Cache->FreeSize - 
Cache->Stride),
+                         1),
+                   TRUE);
 
     if (!Locked)
         __CacheReleaseLock(Cache);
@@ -646,74 +404,6 @@ done:
     KeLowerIrql(Irql);
 }
 
-static NTSTATUS
-CacheFill(
-    IN  PXENBUS_CACHE   Cache,
-    IN  ULONG           Count
-    )
-{
-    KIRQL               Irql;
-    NTSTATUS            status;
-
-    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
-    __CacheAcquireLock(Cache);
-
-    status = STATUS_SUCCESS;
-    while (Cache->Count < Count) {
-        status = CacheCreateSlab(Cache);
-        if (!NT_SUCCESS(status))
-            break;
-    }
-
-    CacheAudit(Cache);
-
-    __CacheReleaseLock(Cache);
-    KeLowerIrql(Irql);
-
-    return status;
-}
-
-static VOID
-CacheSpill(
-    IN  PXENBUS_CACHE   Cache,
-    IN  ULONG           Count
-    )
-{
-    KIRQL               Irql;
-    PLIST_ENTRY         ListEntry;
-
-    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
-    __CacheAcquireLock(Cache);
-
-    if (Cache->Count <= Count)
-        goto done;
-
-    while (!IsListEmpty(&Cache->SlabList)) {
-        PXENBUS_CACHE_SLAB  Slab;
-
-        // Actual list removal is done in CacheDestroySlab()
-        ListEntry = Cache->SlabList.Blink;
-        ASSERT(ListEntry != &Cache->SlabList);
-
-        Slab = CONTAINING_RECORD(ListEntry, XENBUS_CACHE_SLAB, ListEntry);
-
-        if (Slab->CurrentOccupancy != 0)
-            break;
-
-        ASSERT(Cache->Count >= Slab->MaximumOccupancy);
-        if (Cache->Count - Slab->MaximumOccupancy < Count)
-            break;
-
-        CacheDestroySlab(Cache, Slab);
-    }
-
-    CacheAudit(Cache);
-
-done:
-    __CacheReleaseLock(Cache);
-    KeLowerIrql(Irql);
-}
-
 static FORCEINLINE VOID
 __CacheFlushMagazines(
     IN  PXENBUS_CACHE   Cache
@@ -730,12 +420,16 @@ __CacheFlushMagazines(
         PVOID                   Object;
 
         while ((Object = CacheGetObjectFromMagazine(Magazine)) != NULL) {
-            PXENBUS_CACHE_SLAB  Slab;
+            PXENBUS_CACHE_ENTRY Entry;
+            LONG                ObjectCount;
 
-            Slab = (PXENBUS_CACHE_SLAB)PAGE_ALIGN(Object);
-            ASSERT3U(Slab->Magic, ==, XENBUS_CACHE_SLAB_MAGIC);
+            Entry = CONTAINING_RECORD(Object, XENBUS_CACHE_ENTRY, Buffer);
+            ASSERT3U(Entry->Magic, ==, XENBUS_CACHE_SLAB_MAGIC);
 
-            CachePutObjectToSlab(Slab, Object);
+            InsertHeadList(&Cache->FreeList, &Entry->ListEntry);
+            ObjectCount = InterlockedIncrement(&Cache->FreeSize);
+            if (ObjectCount > Cache->FreeMaximum)
+                Cache->FreeMaximum = ObjectCount;
         }
     }
 
@@ -750,6 +444,7 @@ CacheCreate(
     IN  ULONG               Size,
     IN  ULONG               Reservation,
     IN  ULONG               Cap,
+    IN  ULONG               Stride,
     IN  NTSTATUS            (*Ctor)(PVOID, PVOID),
     IN  VOID                (*Dtor)(PVOID, PVOID),
     IN  VOID                (*AcquireLock)(PVOID),
@@ -781,24 +476,28 @@ CacheCreate(
 
     if (Cap == 0)
         Cap = ULONG_MAX;
+    if (Stride == 0)
+        Stride = 1;
+    if (Stride > Cap)
+        Stride = Cap;
 
     (*Cache)->Size = Size;
     (*Cache)->Reservation = Reservation;
     (*Cache)->Cap = Cap;
+    (*Cache)->Stride = Stride;
     (*Cache)->Ctor = Ctor;
     (*Cache)->Dtor = Dtor;
     (*Cache)->AcquireLock = AcquireLock;
     (*Cache)->ReleaseLock = ReleaseLock;
     (*Cache)->Argument = Argument;
 
-    InitializeListHead(&(*Cache)->SlabList);
-    (*Cache)->Cursor = &(*Cache)->SlabList;
+    InitializeListHead(&(*Cache)->FreeList);
 
     status = STATUS_INVALID_PARAMETER;
     if ((*Cache)->Reservation > (*Cache)->Cap)
         goto fail3;
 
-    status = CacheFill(*Cache, (*Cache)->Reservation);
+    status = CacheFill(*Cache, (*Cache)->Reservation, FALSE);
     if (!NT_SUCCESS(status))
         goto fail4;
 
@@ -822,7 +521,7 @@ fail5:
 
     (*Cache)->MagazineCount = 0;
 
-    CacheSpill(*Cache, 0);
+    CacheSpill(*Cache, 0, FALSE);
 
 fail4:
     Error("fail4\n");
@@ -830,15 +529,15 @@ fail4:
 fail3:
     Error("fail3\n");
 
-    (*Cache)->Cursor = NULL;
-    ASSERT(IsListEmpty(&(*Cache)->SlabList));
-    RtlZeroMemory(&(*Cache)->SlabList, sizeof (LIST_ENTRY));
+    ASSERT(IsListEmpty(&(*Cache)->FreeList));
+    RtlZeroMemory(&(*Cache)->FreeList, sizeof (LIST_ENTRY));
 
     (*Cache)->Argument = NULL;
     (*Cache)->ReleaseLock = NULL;
     (*Cache)->AcquireLock = NULL;
     (*Cache)->Dtor = NULL;
     (*Cache)->Ctor = NULL;
+    (*Cache)->Stride = 0;
     (*Cache)->Cap = 0;
     (*Cache)->Reservation = 0;
     (*Cache)->Size = 0;
@@ -876,6 +575,36 @@ CacheCreateVersion1(
                        Size,
                        Reservation,
                        0,
+                       0,
+                       Ctor,
+                       Dtor,
+                       AcquireLock,
+                       ReleaseLock,
+                       Argument,
+                       Cache);
+}
+
+static NTSTATUS
+CacheCreateVersion2(
+    IN  PINTERFACE          Interface,
+    IN  const CHAR          *Name,
+    IN  ULONG               Size,
+    IN  ULONG               Reservation,
+    IN  ULONG               Cap,
+    IN  NTSTATUS            (*Ctor)(PVOID, PVOID),
+    IN  VOID                (*Dtor)(PVOID, PVOID),
+    IN  VOID                (*AcquireLock)(PVOID),
+    IN  VOID                (*ReleaseLock)(PVOID),
+    IN  PVOID               Argument,
+    OUT PXENBUS_CACHE       *Cache
+    )
+{
+    return CacheCreate(Interface,
+                       Name,
+                       Size,
+                       Reservation,
+                       Cap,
+                       0,
                        Ctor,
                        Dtor,
                        AcquireLock,
@@ -908,17 +637,16 @@ CacheDestroy(
     Cache->Magazine = NULL;
     Cache->MagazineCount = 0;
 
-    CacheSpill(Cache, 0);
+    CacheSpill(Cache, 0, FALSE);
 
     ASSERT(Cache->CurrentObjects == 0);
     Cache->MaximumObjects = 0;
 
-    ASSERT(Cache->CurrentSlabs == 0);
-    Cache->MaximumSlabs = 0;
+    ASSERT(Cache->FreeSize == 0);
+    Cache->FreeSize = 0;
 
-    Cache->Cursor = NULL;
-    ASSERT(IsListEmpty(&Cache->SlabList));
-    RtlZeroMemory(&Cache->SlabList, sizeof (LIST_ENTRY));
+    ASSERT(IsListEmpty(&Cache->FreeList));
+    RtlZeroMemory(&Cache->FreeList, sizeof (LIST_ENTRY));
 
     Cache->Argument = NULL;
     Cache->ReleaseLock = NULL;
@@ -963,14 +691,15 @@ CacheDebugCallback(
 
             XENBUS_DEBUG(Printf,
                          &Context->DebugInterface,
-                         "- %s: Count = %d, Reservation = %d, Objects = %d / 
%d, Slabs = %d / %d\n",
+                         "- %s: Reservation = %d, Cap = %d, Stride = 
+ %d, Objects = %d / %d, Free = %d / %d\n",
                          Cache->Name,
-                         Cache->Count,
                          Cache->Reservation,
+                         Cache->Cap,
+                         Cache->Stride,
                          Cache->CurrentObjects,
                          Cache->MaximumObjects,
-                         Cache->CurrentSlabs,
-                         Cache->MaximumSlabs);
+                         Cache->FreeSize,
+                         Cache->FreeMaximum);
         }
     }
 }
@@ -1024,11 +753,15 @@ CacheMonitor(
 
             Cache = CONTAINING_RECORD(ListEntry, XENBUS_CACHE, ListEntry);
 
-            if (Cache->Count < Cache->Reservation)
-                CacheFill(Cache, Cache->Reservation);
-            else if (Cache->Count > Cache->Reservation)
+            if (Cache->CurrentObjects + Cache->FreeSize < 
(LONG)Cache->Reservation)
+                CacheFill(Cache,
+                          __max((Cache->Reservation - Cache->CurrentObjects), 
Cache->Stride),
+                          FALSE);
+            else if (Cache->CurrentObjects + Cache->FreeSize > 
+ (LONG)Cache->Reservation)
                 CacheSpill(Cache,
-                           __max(Cache->Reservation, (Cache->Count / 2)));
+                           __max((LONG)Cache->Reservation,
+                                 (Cache->CurrentObjects + Cache->FreeSize / 
2)),
+                           FALSE);
         }
 
 loop:
@@ -1136,12 +869,22 @@ static struct _XENBUS_CACHE_INTERFACE_V2 
CacheInterfaceVersion2 = {
     { sizeof (struct _XENBUS_CACHE_INTERFACE_V2), 2, NULL, NULL, NULL },
     CacheAcquire,
     CacheRelease,
+    CacheCreateVersion2,
+    CacheGet,
+    CachePut,
+    CacheDestroy
+};
+
+static struct _XENBUS_CACHE_INTERFACE_V3 CacheInterfaceVersion3 = {
+    { sizeof (struct _XENBUS_CACHE_INTERFACE_V3), 3, NULL, NULL, NULL },
+    CacheAcquire,
+    CacheRelease,
     CacheCreate,
     CacheGet,
     CachePut,
     CacheDestroy
 };
-                     
+
 NTSTATUS
 CacheInitialize(
     IN  PXENBUS_FDO             Fdo,
@@ -1240,6 +983,23 @@ CacheGetInterface(
         status = STATUS_SUCCESS;
         break;
     }
+    case 3: {
+        struct _XENBUS_CACHE_INTERFACE_V3   *CacheInterface;
+
+        CacheInterface = (struct _XENBUS_CACHE_INTERFACE_V3 
+ *)Interface;
+
+        status = STATUS_BUFFER_OVERFLOW;
+        if (Size < sizeof (struct _XENBUS_CACHE_INTERFACE_V3))
+            break;
+
+        *CacheInterface = CacheInterfaceVersion3;
+
+        ASSERT3U(Interface->Version, ==, Version);
+        Interface->Context = Context;
+
+        status = STATUS_SUCCESS;
+        break;
+    }
     default:
         status = STATUS_NOT_SUPPORTED;
         break;
diff --git a/src/xenbus/gnttab.c b/src/xenbus/gnttab.c index 33373c2..10920b8 
100644
--- a/src/xenbus/gnttab.c
+++ b/src/xenbus/gnttab.c
@@ -363,6 +363,7 @@ GnttabCreateCache(
                           sizeof (XENBUS_GNTTAB_ENTRY),
                           Reservation,
                           Cap,
+                          1,
                           GnttabEntryCtor,
                           GnttabEntryDtor,
                           GnttabAcquireLock,
--
2.32.0.windows.1






 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.