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

[RFC PATCH] Use a FreeList for CACHE entries


  • To: <win-pv-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Owen Smith <owen.smith@xxxxxxxxxx>
  • Date: Thu, 28 Jul 2022 15:53:07 +0100
  • Authentication-results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none
  • Cc: Owen Smith <owen.smith@xxxxxxxxxx>
  • Delivery-date: Thu, 28 Jul 2022 14:53:25 +0000
  • Ironport-data: A9a23:EEew86AWtKLWhRVW/2Djw5YqxClBgxIJ4kV8jS/XYbTApDh20DEEm zNOXDuBaPjeZmL9et9ya4qxoU9XsJPTzNZhQQY4rX1jcSlH+JHPbTi7wuYcHM8wwunrFh8PA xA2M4GYRCwMZiaA4E3ratANlFEkvYmQXL3wFeXYDS54QA5gWU8JhAlq3uU0meaEu/Dga++2k Y608pa31GONgWYuaDpLsfLb8nuDgdyp0N8mlg1mDRx0lAe2e0k9VPo3Oay3Jn3kdYhYdsbSq zHrlezREsvxpn/BO/v9+lrJWhRiro36ZGBivkF+Sam66iWukwRpukoN2FjwXm8M49mBt4gZJ NygLvVcQy9xVkHHsLx1vxW1j0iSlECJkVPKCSHXjCCd86HJW2np3fJsTxgqAdAJ3dR4IWNSq tgGMAlYO3hvh8ruqF66Yuxlh8BlJ8j3JoIP/HpnyFk1D95/H8qFGf+To4YFgnFg3aiiHt6HD yYdQT1wYRKGeBhOJlc/A5Mihua4wHL4dlW0rXrK+/pqvzWPk2Sd1pDtEt/PYPyJGflOm1yfu Gjr2WbFXj8VYYn3JT2trSv3276ncTnAcJ0TEvig6LtmjUOewkQXCQYKTh2rrP+hkEm8VtlDb UsO9UITQbMarRLxCIOnBlvh/SDC7kV0t8ds//MS+gSTzaiXuhqlW3kjfDFhasB5kNEufGl/v rOWpO8FFQCDoZXMFy/Np+rF82/rUcQGBTRcPHFZFGPp9/Gm+dhu1UyXE76PBYbv1rXI9SfML ydmRcTUr5EaloY12qqy5jgraBr898GSHmbZCug6N19JDz+Vh6b/PuREEXCBsZ59wH+xFzFtR kQslcmE9/wpBpqQjiGLS+hlNOj3uqbUbGON0QA+RcRJG9GRF5mLJNo43d2DDB0xbpZslcHBO yc/Rj+9FLcMZSD3PMebkqq6CtgwzLiIKOkJosv8N4MWCrAsJVDvwc2bTRTPt4wbuBRzzPpX1 FbyWZrEMEv2/ow8nWTuFrhDiuJ6rs38rEuKLa3GI92c+eL2TBaopX0tajNisshRAHu4nTjo
  • Ironport-hdrordr: A9a23:xZ6iYKONfQY73MBcTsujsMiBIKoaSvp037Eqv3ofdfUzSL38qy nOpoV96faaslcssR0b9OxoW5PwI080l6QU3WB5B97LN2PbUQOTXeVfBODZrQEIdReTygck79 YCT5RD
  • List-id: Developer list for the Windows PV Drivers subproject <win-pv-devel.lists.xenproject.org>

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®.