kernel - Improve slab cleanup performance
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 19 Nov 2014 19:55:46 +0000 (11:55 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 19 Nov 2014 19:59:14 +0000 (11:59 -0800)
* Convert ZoneAry[], FreeZones, and FreeOVZones from singly linked lists
  to doubly linked LISTs.  SLZone structure changes size but globaldata
  should stay the same.

* Primarily affects slab_cleanup() which appears to be able to eat
  an excessive amount of cpu on monster (systems with large amounts
  of memory), and may fix a spin lock timeout panic.

* We may need some more work to moderate the amount of time slab_cleanup()
  takes.

sys/kern/kern_slaballoc.c
sys/sys/slaballoc.h

index 28e014c..77386dd 100644 (file)
@@ -510,24 +510,23 @@ SLZone *
 check_zone_free(SLGlobalData *slgd, SLZone *z)
 {
     if (z->z_NFree == z->z_NMax &&
-       (z->z_Next || slgd->ZoneAry[z->z_ZoneIndex] != z) &&
+       (z->z_Next || LIST_FIRST(&slgd->ZoneAry[z->z_ZoneIndex]) != z) &&
        z->z_RCount == 0
     ) {
-       SLZone **pz;
+       SLZone *znext;
        int *kup;
 
-       for (pz = &slgd->ZoneAry[z->z_ZoneIndex]; z != *pz; pz = &(*pz)->z_Next)
-           ;
-       *pz = z->z_Next;
+       znext = LIST_NEXT(z, z_Entry);
+       LIST_REMOVE(z, z_Entry);
+
        z->z_Magic = -1;
-       z->z_Next = slgd->FreeZones;
-       slgd->FreeZones = z;
+       LIST_INSERT_HEAD(&slgd->FreeZones, z, z_Entry);
        ++slgd->NFreeZones;
        kup = btokup(z);
        *kup = 0;
-       z = *pz;
+       z = znext;
     } else {
-       z = z->z_Next;
+       z = LIST_NEXT(z, z_Entry);
     }
     return z;
 }
@@ -677,8 +676,8 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
        if (slgd->NFreeZones > ZoneRelsThresh) {        /* crit sect race */
            int *kup;
 
-           z = slgd->FreeZones;
-           slgd->FreeZones = z->z_Next;
+           z = LIST_FIRST(&slgd->FreeZones);
+           LIST_REMOVE(z, z_Entry);
            --slgd->NFreeZones;
            kup = btokup(z);
            *kup = 0;
@@ -691,13 +690,13 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
     /*
      * XXX handle oversized frees that were queued from kfree().
      */
-    while (slgd->FreeOvZones && (flags & M_RNOWAIT) == 0) {
+    while (LIST_FIRST(&slgd->FreeOvZones) && (flags & M_RNOWAIT) == 0) {
        crit_enter();
-       if ((z = slgd->FreeOvZones) != NULL) {
+       if ((z = LIST_FIRST(&slgd->FreeOvZones)) != NULL) {
            vm_size_t tsize;
 
            KKASSERT(z->z_Magic == ZALLOC_OVSZ_MAGIC);
-           slgd->FreeOvZones = z->z_Next;
+           LIST_REMOVE(z, z_Entry);
            tsize = z->z_ChunkSize;
            kmem_slab_free(z, tsize);   /* may block */
            atomic_add_int(&ZoneBigAlloc, -(int)tsize / 1024);
@@ -743,7 +742,7 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
     KKASSERT(zi < NZONES);
     crit_enter();
 
-    if ((z = slgd->ZoneAry[zi]) != NULL) {
+    if ((z = LIST_FIRST(&slgd->ZoneAry[zi])) != NULL) {
        /*
         * Locate a chunk - we have to have at least one.  If this is the
         * last chunk go ahead and do the work to retrieve chunks freed
@@ -773,8 +772,7 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
             * Clear RSignal
             */
            if (z->z_NFree == 0) {
-               slgd->ZoneAry[zi] = z->z_Next;
-               z->z_Next = NULL;
+               LIST_REMOVE(z, z_Entry);
            } else {
                z->z_RSignal = 0;
            }
@@ -837,8 +835,8 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
        int off;
        int *kup;
 
-       if ((z = slgd->FreeZones) != NULL) {
-           slgd->FreeZones = z->z_Next;
+       if ((z = LIST_FIRST(&slgd->FreeZones)) != NULL) {
+           LIST_REMOVE(z, z_Entry);
            --slgd->NFreeZones;
            bzero(z, sizeof(SLZone));
            z->z_Flags |= SLZF_UNOTZEROD;
@@ -886,8 +884,7 @@ kmalloc(unsigned long size, struct malloc_type *type, int flags)
        bzero(z->z_Sources, sizeof(z->z_Sources));
 #endif
        chunk = (SLChunk *)(z->z_BasePtr + z->z_UIndex * size);
-       z->z_Next = slgd->ZoneAry[zi];
-       slgd->ZoneAry[zi] = z;
+       LIST_INSERT_HEAD(&slgd->ZoneAry[zi], z, z_Entry);
        if ((z->z_Flags & SLZF_UNOTZEROD) == 0) {
            flags &= ~M_ZERO;   /* already zero'd */
            flags |= M_PASSIVE_ZERO;
@@ -1096,8 +1093,7 @@ kfree_remote(void *ptr)
      */
     clean_zone_rchunks(z);
     if (z->z_NFree && nfree == 0) {
-       z->z_Next = slgd->ZoneAry[z->z_ZoneIndex];
-       slgd->ZoneAry[z->z_ZoneIndex] = z;
+       LIST_INSERT_HEAD(&slgd->ZoneAry[z->z_ZoneIndex], z, z_Entry);
     }
 
     /*
@@ -1111,21 +1107,14 @@ kfree_remote(void *ptr)
      * zone.
      */
     if (z->z_NFree == z->z_NMax &&
-       (z->z_Next || slgd->ZoneAry[z->z_ZoneIndex] != z) &&
+       (z->z_Next || LIST_FIRST(&slgd->ZoneAry[z->z_ZoneIndex]) != z) &&
        z->z_RCount == 0
     ) {
-       SLZone **pz;
        int *kup;
 
-       for (pz = &slgd->ZoneAry[z->z_ZoneIndex];
-            z != *pz;
-            pz = &(*pz)->z_Next) {
-           ;
-       }
-       *pz = z->z_Next;
+       LIST_REMOVE(z, z_Entry);
        z->z_Magic = -1;
-       z->z_Next = slgd->FreeZones;
-       slgd->FreeZones = z;
+       LIST_INSERT_HEAD(&slgd->FreeZones, z, z_Entry);
        ++slgd->NFreeZones;
        kup = btokup(z);
        *kup = 0;
@@ -1209,9 +1198,9 @@ kfree(void *ptr, struct malloc_type *type)
            logmemory(free_ovsz_delayed, ptr, type, size, 0);
            z = (SLZone *)ptr;
            z->z_Magic = ZALLOC_OVSZ_MAGIC;
-           z->z_Next = slgd->FreeOvZones;
            z->z_ChunkSize = size;
-           slgd->FreeOvZones = z;
+
+           LIST_INSERT_HEAD(&slgd->FreeOvZones, z, z_Entry);
            crit_exit();
        } else {
            crit_exit();
@@ -1349,8 +1338,7 @@ kfree(void *ptr, struct malloc_type *type)
      * must be added back onto the appropriate list.
      */
     if (z->z_NFree++ == 0) {
-       z->z_Next = slgd->ZoneAry[z->z_ZoneIndex];
-       slgd->ZoneAry[z->z_ZoneIndex] = z;
+       LIST_INSERT_HEAD(&slgd->ZoneAry[z->z_ZoneIndex], z, z_Entry);
     }
 
     --type->ks_inuse[z->z_Cpu];
@@ -1374,9 +1362,9 @@ slab_cleanup(void)
 
     crit_enter();
     for (i = 0; i < NZONES; ++i) {
-       if ((z = slgd->ZoneAry[i]) == NULL)
+       if ((z = LIST_FIRST(&slgd->ZoneAry[i])) == NULL)
                continue;
-       z = z->z_Next;
+       z = LIST_NEXT(z, z_Entry);
 
        /*
         * Scan zones starting with the second zone in each list.
@@ -1385,6 +1373,8 @@ slab_cleanup(void)
            /*
             * Shift all RChunks to the end of the LChunks list.  This is
             * an O(1) operation.
+            *
+            * Then free the zone if possible.
             */
            clean_zone_rchunks(z);
            z = check_zone_free(slgd, z);
index dbf5119..8afdb19 100644 (file)
@@ -97,6 +97,7 @@ typedef struct SLZone {
     __int32_t  z_Magic;        /* magic number for sanity check */
     int                z_Cpu;          /* which cpu owns this zone? */
     struct globaldata *z_CpuGd;        /* which cpu owns this zone? */
+    LIST_ENTRY(SLZone) z_Entry;        /* ZoneAry[] if z_NFree!=0, else Free*Zones */
     struct SLZone *z_Next;     /* ZoneAry[] link if z_NFree non-zero */
     int                z_NFree;        /* total free chunks / ualloc space in zone */
     int                z_NMax;         /* maximum free chunks */
@@ -123,10 +124,13 @@ typedef struct SLZone {
 
 #define SLZF_UNOTZEROD         0x0001
 
+LIST_HEAD(SLZoneList, SLZone);         /* NOTE: Assumes NULL initialization */
+typedef struct SLZoneList SLZoneList;
+
 typedef struct SLGlobalData {
-    SLZone     *ZoneAry[NZONES];       /* linked list of zones NFree > 0 */
-    SLZone     *FreeZones;             /* whole zones that have become free */
-    SLZone     *FreeOvZones;           /* oversized zones */
+    SLZoneList ZoneAry[NZONES];        /* linked list of zones NFree > 0 */
+    SLZoneList FreeZones;              /* whole zones that have become free */
+    SLZoneList FreeOvZones;            /* oversized zones */
     int                NFreeZones;             /* free zone count */
     int                JunkIndex;
     struct malloc_type ZoneInfo;       /* stats on meta-zones allocated */