Author: markj
Date: Mon Oct 22 16:16:42 2018
New Revision: 339599
URL: https://svnweb.freebsd.org/changeset/base/339599

Log:
  Don't import 0 into vmem quantum caches.
  
  vmem uses UMA cache zones to implement the quantum cache.  Since
  uma_zalloc() returns 0 (NULL) to signal an allocation failure, UMA
  should not be used to cache resource 0.  Fix this by ensuring that 0 is
  never cached in UMA in the first place, and by modifying vmem_alloc()
  to fall back to a search of the free lists if the cache is depleted,
  rather than blocking in qc_import().
  
  Reported by and discussed with:       Brett Gutstein <bgutst...@rice.edu>
  Reviewed by:  alc
  MFC after:    2 weeks
  Differential Revision:        https://reviews.freebsd.org/D17483

Modified:
  head/sys/kern/subr_vmem.c
  head/sys/sys/vmem.h

Modified: head/sys/kern/subr_vmem.c
==============================================================================
--- head/sys/kern/subr_vmem.c   Mon Oct 22 16:09:01 2018        (r339598)
+++ head/sys/kern/subr_vmem.c   Mon Oct 22 16:16:42 2018        (r339599)
@@ -504,6 +504,9 @@ bt_insfree(vmem_t *vm, bt_t *bt)
 
 /*
  * Import from the arena into the quantum cache in UMA.
+ *
+ * We use VMEM_ADDR_QCACHE_MIN instead of 0: uma_zalloc() returns 0 to indicate
+ * failure, so UMA can't be used to cache a resource with value 0.
  */
 static int
 qc_import(void *arg, void **store, int cnt, int domain, int flags)
@@ -512,19 +515,16 @@ qc_import(void *arg, void **store, int cnt, int domain
        vmem_addr_t addr;
        int i;
 
+       KASSERT((flags & M_WAITOK) == 0, ("blocking allocation"));
+
        qc = arg;
-       if ((flags & VMEM_FITMASK) == 0)
-               flags |= M_BESTFIT;
        for (i = 0; i < cnt; i++) {
                if (vmem_xalloc(qc->qc_vmem, qc->qc_size, 0, 0, 0,
-                   VMEM_ADDR_MIN, VMEM_ADDR_MAX, flags, &addr) != 0)
+                   VMEM_ADDR_QCACHE_MIN, VMEM_ADDR_MAX, flags, &addr) != 0)
                        break;
                store[i] = (void *)addr;
-               /* Only guarantee one allocation. */
-               flags &= ~M_WAITOK;
-               flags |= M_NOWAIT;
        }
-       return i;
+       return (i);
 }
 
 /*
@@ -1123,15 +1123,20 @@ vmem_alloc(vmem_t *vm, vmem_size_t size, int flags, vm
                WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "vmem_alloc");
 
        if (size <= vm->vm_qcache_max) {
+               /*
+                * Resource 0 cannot be cached, so avoid a blocking allocation
+                * in qc_import() and give the vmem_xalloc() call below a chance
+                * to return 0.
+                */
                qc = &vm->vm_qcache[(size - 1) >> vm->vm_quantum_shift];
-               *addrp = (vmem_addr_t)uma_zalloc(qc->qc_cache, flags);
-               if (*addrp == 0)
-                       return (ENOMEM);
-               return (0);
+               *addrp = (vmem_addr_t)uma_zalloc(qc->qc_cache,
+                   (flags & ~M_WAITOK) | M_NOWAIT);
+               if (__predict_true(*addrp != 0))
+                       return (0);
        }
 
-       return vmem_xalloc(vm, size, 0, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX,
-           flags, addrp);
+       return (vmem_xalloc(vm, size, 0, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX,
+           flags, addrp));
 }
 
 int
@@ -1263,7 +1268,8 @@ vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t si
        qcache_t *qc;
        MPASS(size > 0);
 
-       if (size <= vm->vm_qcache_max) {
+       if (size <= vm->vm_qcache_max &&
+           __predict_true(addr >= VMEM_ADDR_QCACHE_MIN)) {
                qc = &vm->vm_qcache[(size - 1) >> vm->vm_quantum_shift];
                uma_zfree(qc->qc_cache, (void *)addr);
        } else

Modified: head/sys/sys/vmem.h
==============================================================================
--- head/sys/sys/vmem.h Mon Oct 22 16:09:01 2018        (r339598)
+++ head/sys/sys/vmem.h Mon Oct 22 16:16:42 2018        (r339599)
@@ -41,8 +41,9 @@ typedef struct vmem vmem_t;
 typedef uintptr_t      vmem_addr_t;
 typedef size_t         vmem_size_t;
 
-#define        VMEM_ADDR_MIN   0
-#define        VMEM_ADDR_MAX   (~(vmem_addr_t)0)
+#define        VMEM_ADDR_MIN           0
+#define        VMEM_ADDR_QCACHE_MIN    1
+#define        VMEM_ADDR_MAX           (~(vmem_addr_t)0)
 
 typedef int (vmem_import_t)(void *, vmem_size_t, int, vmem_addr_t *);
 typedef void (vmem_release_t)(void *, vmem_addr_t, vmem_size_t);
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to