]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_malloc.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_malloc.c
index cf158f9b22e3dd69b30e9d5bf672d0b021409f5d..199f0eb7acd533df98313584d59243b8721f2105 100644 (file)
@@ -1,23 +1,29 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */
 /*
  *
  *     @(#)kern_malloc.c       8.4 (Berkeley) 5/20/95
  */
+/*
+ * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ */
+
+#include <kern/zalloc.h>
+#include <kern/kalloc.h>
 
-#include <sys/param.h>
 #include <sys/malloc.h>
+#include <sys/sysctl.h>
 
-#include <sys/socket.h>
-#include <sys/socketvar.h>
+#include <libkern/libkern.h>
 
-#include <net/route.h>
+ZONE_VIEW_DEFINE(ZV_NAMEI, "vfs.namei", KHEAP_ID_DATA_BUFFERS, MAXPATHLEN);
 
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
+static void *
+__MALLOC_ext(
+       size_t          size,
+       int             type,
+       int             flags,
+       vm_allocation_site_t *site,
+       kalloc_heap_t   heap)
+{
+       void    *addr = NULL;
 
-#include <sys/proc.h>
-#include <sys/mount.h>
-#include <sys/vnode.h>
-#include <sys/namei.h>
-#include <sys/file.h>
-#include <sys/filedesc.h>
-#include <sys/tty.h>
-#include <sys/quota.h>
+       if (type >= M_LAST) {
+               panic("_malloc TYPE");
+       }
+
+       if (size == 0) {
+               return NULL;
+       }
 
-#include <ufs/ufs/inode.h>
+       static_assert(sizeof(vm_size_t) == sizeof(size_t));
+       static_assert(M_WAITOK == Z_WAITOK);
+       static_assert(M_NOWAIT == Z_NOWAIT);
+       static_assert(M_ZERO == Z_ZERO);
 
-#include <hfs/hfs_cnode.h>
-#include <isofs/cd9660/cd9660_node.h>
+       addr = kalloc_ext(heap, size,
+           flags & (M_WAITOK | M_NOWAIT | M_ZERO), site).addr;
+       if (__probable(addr)) {
+               return addr;
+       }
 
-#include <miscfs/volfs/volfs.h>
+       if (flags & (M_NOWAIT | M_NULL)) {
+               return NULL;
+       }
 
-#include <nfs/rpcv2.h>
-#include <nfs/nfsproto.h>
-#include <nfs/nfsnode.h>
-#include <nfs/nfsmount.h>
-#include <nfs/nqnfs.h>
+       /*
+        * We get here when the caller told us to block waiting for memory, but
+        * kalloc said there's no memory left to get.  Generally, this means there's a
+        * leak or the caller asked for an impossibly large amount of memory. If the caller
+        * is expecting a NULL return code then it should explicitly set the flag M_NULL.
+        * If the caller isn't expecting a NULL return code, we just panic. This is less
+        * than ideal, but returning NULL when the caller isn't expecting it doesn't help
+        * since the majority of callers don't check the return value and will just
+        * dereference the pointer and trap anyway.  We may as well get a more
+        * descriptive message out while we can.
+        */
+       panic("_MALLOC: kalloc returned NULL (potential leak), size %llu", (uint64_t) size);
+}
 
-#include <mach/mach_types.h>
+void *
+__MALLOC(size_t size, int type, int flags, vm_allocation_site_t *site)
+{
+       return __MALLOC_ext(size, type, flags, site, KHEAP_DEFAULT);
+}
 
-#include <kern/zalloc.h>
-#include <kern/kalloc.h>
+void *
+__REALLOC(
+       void            *addr,
+       size_t          size,
+       int             type __unused,
+       int             flags,
+       vm_allocation_site_t *site)
+{
+       addr = kheap_realloc_addr(KHEAP_DEFAULT, addr, size,
+           flags & (M_WAITOK | M_NOWAIT | M_ZERO), site).addr;
 
-struct kmemstats kmemstats[M_LAST];
-char *memname[] = INITKMEMNAMES;
-
-struct kmzones {
-       size_t          kz_elemsize;
-       void            *kz_zalloczone;
-#define        KMZ_CREATEZONE          ((void *)-2)
-#define KMZ_LOOKUPZONE         ((void *)-1)
-#define KMZ_MALLOC             ((void *)0)
-#define        KMZ_SHAREZONE           ((void *)1)
-} kmzones[M_LAST] = {
-#define        SOS(sname)      sizeof (struct sname)
-#define SOX(sname)     -1
-       -1,             0,                      /* 0 M_FREE */
-       MSIZE,          KMZ_CREATEZONE,         /* 1 M_MBUF */
-       0,              KMZ_MALLOC,             /* 2 M_DEVBUF */
-       SOS(socket),    KMZ_CREATEZONE,         /* 3 M_SOCKET */
-       SOS(inpcb),     KMZ_LOOKUPZONE,         /* 4 M_PCB */
-       M_MBUF,         KMZ_SHAREZONE,          /* 5 M_RTABLE */
-       M_MBUF,         KMZ_SHAREZONE,          /* 6 M_HTABLE */
-       M_MBUF,         KMZ_SHAREZONE,          /* 7 M_FTABLE */
-       SOS(rusage),    KMZ_CREATEZONE,         /* 8 M_ZOMBIE */
-       0,              KMZ_MALLOC,             /* 9 M_IFADDR */
-       M_MBUF,         KMZ_SHAREZONE,          /* 10 M_SOOPTS */
-       0,              KMZ_MALLOC,             /* 11 M_SONAME */
-       MAXPATHLEN,     KMZ_CREATEZONE,         /* 12 M_NAMEI */
-       0,              KMZ_MALLOC,             /* 13 M_GPROF */
-       0,              KMZ_MALLOC,             /* 14 M_IOCTLOPS */
-       0,              KMZ_MALLOC,             /* 15 M_MAPMEM */
-       SOS(ucred),     KMZ_CREATEZONE,         /* 16 M_CRED */
-       SOS(pgrp),      KMZ_CREATEZONE,         /* 17 M_PGRP */
-       SOS(session),   KMZ_CREATEZONE,         /* 18 M_SESSION */
-       SOS(iovec),     KMZ_LOOKUPZONE,         /* 19 M_IOV */
-       SOS(mount),     KMZ_CREATEZONE,         /* 20 M_MOUNT */
-       0,              KMZ_MALLOC,             /* 21 M_FHANDLE */
-       SOS(nfsreq),    KMZ_CREATEZONE,         /* 22 M_NFSREQ */
-       SOS(nfsmount),  KMZ_CREATEZONE,         /* 23 M_NFSMNT */
-       SOS(nfsnode),   KMZ_CREATEZONE,         /* 24 M_NFSNODE */
-       SOS(vnode),     KMZ_CREATEZONE,         /* 25 M_VNODE */
-       SOS(namecache), KMZ_CREATEZONE,         /* 26 M_CACHE */
-       SOX(dquot),     KMZ_LOOKUPZONE,         /* 27 M_DQUOT */
-       SOX(ufsmount),  KMZ_LOOKUPZONE,         /* 28 M_UFSMNT */
-       0,              KMZ_MALLOC,             /* 29 M_CGSUM */
-       0,              KMZ_MALLOC,             /* 30 M_VMMAP */
-       0,              KMZ_MALLOC,             /* 31 M_VMMAPENT */
-       0,              KMZ_MALLOC,             /* 32 M_VMOBJ */
-       0,              KMZ_MALLOC,             /* 33 M_VMOBJHASH */
-       0,              KMZ_MALLOC,             /* 34 M_VMPMAP */
-       0,              KMZ_MALLOC,             /* 35 M_VMPVENT */
-       0,              KMZ_MALLOC,             /* 36 M_VMPAGER */
-       0,              KMZ_MALLOC,             /* 37 M_VMPGDATA */
-       SOS(file),      KMZ_CREATEZONE,         /* 38 M_FILE */
-       SOS(filedesc),  KMZ_CREATEZONE,         /* 39 M_FILEDESC */
-       SOX(lockf),     KMZ_CREATEZONE,         /* 40 M_LOCKF */
-       SOS(proc),      KMZ_CREATEZONE,         /* 41 M_PROC */
-       SOS(pcred),     KMZ_CREATEZONE,         /* 42 M_SUBPROC */
-       0,              KMZ_MALLOC,             /* 43 M_SEGMENT */
-       M_FFSNODE,      KMZ_SHAREZONE,          /* 44 M_LFSNODE */
-       SOS(inode),     KMZ_CREATEZONE,         /* 45 M_FFSNODE */
-       M_FFSNODE,      KMZ_SHAREZONE,          /* 46 M_MFSNODE */
-       SOS(nqlease),   KMZ_CREATEZONE,         /* 47 M_NQLEASE */
-       SOS(nqm),       KMZ_CREATEZONE,         /* 48 M_NQMHOST */
-       0,              KMZ_MALLOC,             /* 49 M_NETADDR */
-       SOX(nfssvc_sock),
-                       KMZ_CREATEZONE,         /* 50 M_NFSSVC */
-       SOS(nfsuid),    KMZ_CREATEZONE,         /* 51 M_NFSUID */
-       SOX(nfsrvcache),
-                       KMZ_CREATEZONE,         /* 52 M_NFSD */
-       SOX(ip_moptions),
-                       KMZ_LOOKUPZONE,         /* 53 M_IPMOPTS */
-       SOX(in_multi),  KMZ_LOOKUPZONE,         /* 54 M_IPMADDR */
-       SOX(ether_multi),
-                       KMZ_LOOKUPZONE,         /* 55 M_IFMADDR */
-       SOX(mrt),       KMZ_CREATEZONE,         /* 56 M_MRTABLE */
-       SOX(iso_mnt),   KMZ_LOOKUPZONE,         /* 57 M_ISOFSMNT */
-       SOS(iso_node),  KMZ_CREATEZONE,         /* 58 M_ISOFSNODE */
-       SOS(nfsrv_descript),
-                       KMZ_CREATEZONE,         /* 59 M_NFSRVDESC */
-       SOS(nfsdmap),   KMZ_CREATEZONE,         /* 60 M_NFSDIROFF */
-       SOS(fhandle),   KMZ_LOOKUPZONE,         /* 61 M_NFSBIGFH */
-       0,              KMZ_MALLOC,             /* 62 M_MSDOSFSMNT */
-       0,              KMZ_MALLOC,             /* 63 M_MSDOSFSFAT */
-       0,              KMZ_MALLOC,             /* 64 M_MSDOSFSNODE */
-       SOS(tty),       KMZ_CREATEZONE,         /* 65 M_TTYS */
-       0,              KMZ_MALLOC,             /* 66 M_EXEC */
-       0,              KMZ_MALLOC,             /* 67 M_MISCFSMNT */
-       0,              KMZ_MALLOC,             /* 68 M_MISCFSNODE */
-       0,              KMZ_MALLOC,             /* 69 M_ADOSFSMNT */
-       0,              KMZ_MALLOC,             /* 70 M_ADOSFSNODE */
-       0,              KMZ_MALLOC,             /* 71 M_ANODE */
-       SOX(buf),       KMZ_CREATEZONE,         /* 72 M_BUFHDR */
-       (NDFILE * OFILESIZE),
-                       KMZ_CREATEZONE,         /* 73 M_OFILETABL */
-       MCLBYTES,       KMZ_CREATEZONE,         /* 74 M_MCLUST */
-       SOX(hfsmount),  KMZ_LOOKUPZONE,         /* 75 M_HFSMNT */
-       SOS(cnode),     KMZ_CREATEZONE,         /* 76 M_HFSNODE */
-       SOS(filefork),  KMZ_CREATEZONE,         /* 77 M_HFSFORK */
-       SOX(volfs_mntdata),     KMZ_LOOKUPZONE,         /* 78 M_VOLFSMNT */
-       SOS(volfs_vndata),      KMZ_CREATEZONE,         /* 79 M_VOLFSNODE */
-       0,              KMZ_MALLOC,             /* 80 M_TEMP */
-       0,              KMZ_MALLOC,             /* 81 M_SECA */
-       0,              KMZ_MALLOC,             /* 82 M_DEVFS */
-       0,              KMZ_MALLOC,             /* 83 M_IPFW */
-       0,              KMZ_MALLOC,             /* 84 M_UDFNODE */
-       0,              KMZ_MALLOC,             /* 85 M_UDFMOUNT */
-       0,              KMZ_MALLOC,             /* 86 M_IP6NDP */
-       0,              KMZ_MALLOC,             /* 87 M_IP6OPT */
-       0,              KMZ_MALLOC,             /* 88 M_IP6MISC */
-       0,              KMZ_MALLOC,             /* 89 M_TSEGQ */
-       0,              KMZ_MALLOC,             /* 90 M_IGMP */
-
-#undef SOS
-#undef SOX
-};
+       if (__probable(addr)) {
+               return addr;
+       }
 
+       if (flags & (M_NOWAIT | M_NULL)) {
+               return NULL;
+       }
 
-/*
- * Initialize the kernel memory allocator
- */
+       panic("_REALLOC: kalloc returned NULL (potential leak), size %llu", (uint64_t) size);
+}
+
+void *
+_MALLOC_external(size_t size, int type, int flags);
+void *
+_MALLOC_external(size_t size, int type, int flags)
+{
+       static vm_allocation_site_t site = {
+               .tag = VM_KERN_MEMORY_KALLOC,
+               .flags = VM_TAG_BT,
+       };
+       return __MALLOC_ext(size, type, flags, &site, KHEAP_KEXT);
+}
+
+void
+_FREE_external(void *addr, int type);
 void
-kmeminit(void)
+_FREE_external(void *addr, int type __unused)
 {
-       struct kmzones  *kmz;
-
-       kmz = kmzones;
-       while (kmz < &kmzones[M_LAST]) {
-/* XXX */
-               if (kmz->kz_elemsize == -1)
-                       ;
-               else
-/* XXX */
-               if (kmz->kz_zalloczone == KMZ_CREATEZONE) {
-                       kmz->kz_zalloczone = zinit(kmz->kz_elemsize,
-                                               1024 * 1024, PAGE_SIZE,
-                                               memname[kmz - kmzones]);
-               }
-               else if (kmz->kz_zalloczone == KMZ_LOOKUPZONE)
-                       kmz->kz_zalloczone = kalloc_zone(kmz->kz_elemsize);
+       /*
+        * hashinit and other functions allocate on behalf of kexts and do not have
+        * a matching hashdestroy, so we sadly have to allow this for now.
+        */
+       kheap_free_addr(KHEAP_ANY, addr);
+}
 
-               kmz++;
-       }
+void
+_FREE_ZONE_external(void *elem, size_t size, int type);
+void
+_FREE_ZONE_external(void *elem, size_t size, int type __unused)
+{
+       (kheap_free)(KHEAP_KEXT, elem, size);
+}
 
-       kmz = kmzones;
-       while (kmz < &kmzones[M_LAST]) {
-/* XXX */
-               if (kmz->kz_elemsize == -1)
-                       ;
-               else
-/* XXX */
-               if (kmz->kz_zalloczone == KMZ_SHAREZONE) {
-                       kmz->kz_zalloczone =
-                               kmzones[kmz->kz_elemsize].kz_zalloczone;
-                       kmz->kz_elemsize =
-                               kmzones[kmz->kz_elemsize].kz_elemsize;
-               }
+#if DEBUG || DEVELOPMENT
+
+extern unsigned int zone_map_jetsam_limit;
+
+static int
+sysctl_zone_map_jetsam_limit SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+       int oldval = 0, val = 0, error = 0;
+
+       oldval = zone_map_jetsam_limit;
+       error = sysctl_io_number(req, oldval, sizeof(int), &val, NULL);
+       if (error || !req->newptr) {
+               return error;
+       }
 
-               kmz++;
+       if (val <= 0 || val > 100) {
+               printf("sysctl_zone_map_jetsam_limit: new jetsam limit value is invalid.\n");
+               return EINVAL;
        }
+
+       zone_map_jetsam_limit = val;
+       return 0;
 }
 
-#define        MDECL(reqlen)                                   \
-union {                                                        \
-       struct  _mhead hdr;                             \
-       char    _m[(reqlen) + sizeof (struct _mhead)];  \
+SYSCTL_PROC(_kern, OID_AUTO, zone_map_jetsam_limit, CTLTYPE_INT | CTLFLAG_RW, 0, 0,
+    sysctl_zone_map_jetsam_limit, "I", "Zone map jetsam limit");
+
+
+extern void get_zone_map_size(uint64_t *current_size, uint64_t *capacity);
+
+static int
+sysctl_zone_map_size_and_capacity SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+       uint64_t zstats[2];
+       get_zone_map_size(&zstats[0], &zstats[1]);
+
+       return SYSCTL_OUT(req, &zstats, sizeof(zstats));
 }
 
-struct _mhead {
-       size_t  mlen;
-       char    dat[0];
-};
+SYSCTL_PROC(_kern, OID_AUTO, zone_map_size_and_capacity,
+    CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+    0, 0, &sysctl_zone_map_size_and_capacity, "Q", "Current size and capacity of the zone map");
 
-#define ZEROSIZETOKEN 0xFADEDFAD
 
-void *_MALLOC(
-       size_t          size,
-       int             type,
-       int             flags)
+extern boolean_t run_zone_test(void);
+
+static int
+sysctl_run_zone_test SYSCTL_HANDLER_ARGS
 {
-       MDECL(size)     *mem;
-       size_t          memsize = sizeof (*mem);
+#pragma unused(oidp, arg1, arg2)
+       /* require setting this sysctl to prevent sysctl -a from running this */
+       if (!req->newptr) {
+               return 0;
+       }
 
-       if (type >= M_LAST)
-               panic("_malloc TYPE");
+       int ret_val = run_zone_test();
+       return SYSCTL_OUT(req, &ret_val, sizeof(ret_val));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, run_zone_test,
+    CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+    0, 0, &sysctl_run_zone_test, "I", "Test zone allocator KPI");
+
+#endif /* DEBUG || DEVELOPMENT */
+
+#if CONFIG_ZLEAKS
 
+SYSCTL_DECL(_kern_zleak);
+SYSCTL_NODE(_kern, OID_AUTO, zleak, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "zleak");
+
+/*
+ * kern.zleak.active
+ *
+ * Show the status of the zleak subsystem (0 = enabled, 1 = active,
+ * and -1 = failed), and if enabled, allow it to be activated immediately.
+ */
+static int
+sysctl_zleak_active SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+       int oldval, val, error;
+
+       val = oldval = get_zleak_state();
+       error = sysctl_handle_int(oidp, &val, 0, req);
+       if (error || !req->newptr) {
+               return error;
+       }
        /*
-        * On zero request we do not return zero as that
-        * could be mistaken for ENOMEM.
+        * Can only be activated if it's off (and not failed.)
+        * Cannot be deactivated once it's on.
         */
-       if (size == 0)
-               return (ZEROSIZETOKEN);
+       if (val == 1 && oldval == 0) {
+               kern_return_t kr = zleak_activate();
 
-       if (flags & M_NOWAIT) {
-               mem = (void *)kalloc_noblock(memsize);
-       } else {
-               mem = (void *)kalloc(memsize);
+               if (KERN_SUCCESS != kr) {
+                       printf("zleak_active: failed to activate "
+                           "live zone leak debugging (%d).\n", kr);
+               }
+       }
+       if (val == 0 && oldval == 1) {
+               printf("zleak_active: active, cannot be disabled.\n");
+               return EINVAL;
        }
-       if (!mem)
-               return (0);
+       return 0;
+}
 
-       mem->hdr.mlen = memsize;
+SYSCTL_PROC(_kern_zleak, OID_AUTO, active,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+    0, 0, sysctl_zleak_active, "I", "zleak activity");
 
-       return  (mem->hdr.dat);
+/*
+ * kern.zleak.max_zonemap_size
+ *
+ * Read the value of the maximum zonemap size in bytes; useful
+ * as the maximum size that zleak.global_threshold and
+ * zleak.zone_threshold should be set to.
+ */
+static int
+sysctl_zleak_max_zonemap_size SYSCTL_HANDLER_ARGS
+{
+       uint64_t zmap_max_size = *(vm_size_t *)arg1;
+
+       return sysctl_handle_quad(oidp, &zmap_max_size, arg2, req);
 }
 
-void _FREE(
-       void            *addr,
-       int             type)
+SYSCTL_PROC(_kern_zleak, OID_AUTO, max_zonemap_size,
+    CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_LOCKED,
+    &zleak_max_zonemap_size, 0,
+    sysctl_zleak_max_zonemap_size, "Q", "zleak max zonemap size");
+
+
+static int
+sysctl_zleak_threshold SYSCTL_HANDLER_ARGS
 {
-       struct _mhead   *hdr;
+#pragma unused(oidp, arg2)
+       int error;
+       uint64_t value = *(vm_size_t *)arg1;
+
+       error = sysctl_io_number(req, value, sizeof(value), &value, NULL);
+
+       if (error || !req->newptr) {
+               return error;
+       }
 
-       if (type >= M_LAST)
-               panic("_free TYPE");
+       if (value > (uint64_t)zleak_max_zonemap_size) {
+               return ERANGE;
+       }
+
+       *(vm_size_t *)arg1 = value;
+       return 0;
+}
+
+/*
+ * kern.zleak.global_threshold
+ *
+ * Set the global zleak threshold size (in bytes).  If the zone map
+ * grows larger than this value, zleaks are automatically activated.
+ *
+ * The default value is set in zleak_init().
+ */
+SYSCTL_PROC(_kern_zleak, OID_AUTO, global_threshold,
+    CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+    &zleak_global_tracking_threshold, 0,
+    sysctl_zleak_threshold, "Q", "zleak global threshold");
+
+/*
+ * kern.zleak.zone_threshold
+ *
+ * Set the per-zone threshold size (in bytes) above which any
+ * zone will automatically start zleak tracking.
+ *
+ * The default value is set in zleak_init().
+ *
+ * Setting this variable will have no effect until zleak tracking is
+ * activated (See above.)
+ */
+SYSCTL_PROC(_kern_zleak, OID_AUTO, zone_threshold,
+    CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+    &zleak_per_zone_tracking_threshold, 0,
+    sysctl_zleak_threshold, "Q", "zleak per-zone threshold");
 
-       if (addr == (void *)ZEROSIZETOKEN)
-               return;
-       if (!addr)
-               return; /* correct (convenient bsd kernel legacy) */
+#endif  /* CONFIG_ZLEAKS */
 
-       hdr = addr; hdr--;
-       kfree((vm_offset_t)hdr, hdr->mlen);
+extern uint64_t get_zones_collectable_bytes(void);
+
+static int
+sysctl_zones_collectable_bytes SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+       uint64_t zones_free_mem = get_zones_collectable_bytes();
+
+       return SYSCTL_OUT(req, &zones_free_mem, sizeof(zones_free_mem));
 }
 
-void *_MALLOC_ZONE(
-       size_t          size,
-       int             type,
-       int             flags)
+SYSCTL_PROC(_kern, OID_AUTO, zones_collectable_bytes,
+    CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+    0, 0, &sysctl_zones_collectable_bytes, "Q", "Collectable memory in zones");
+
+
+#if DEBUG || DEVELOPMENT
+
+static int
+sysctl_zone_gc_replenish_test SYSCTL_HANDLER_ARGS
 {
-       struct kmzones  *kmz;
-       void            *elem;
-
-       if (type >= M_LAST)
-               panic("_malloc_zone TYPE");
-
-       kmz = &kmzones[type];
-       if (kmz->kz_zalloczone == KMZ_MALLOC)
-               panic("_malloc_zone ZONE");
-
-/* XXX */
-       if (kmz->kz_elemsize == -1)
-               panic("_malloc_zone XXX");
-/* XXX */
-       if (size == kmz->kz_elemsize)
-               if (flags & M_NOWAIT) {
-                       elem = (void *)zalloc_noblock(kmz->kz_zalloczone);
-               } else {
-                       elem = (void *)zalloc(kmz->kz_zalloczone);
-               }
-       else
-               if (flags & M_NOWAIT) {
-                       elem = (void *)kalloc_noblock(size);
-               } else {
-                       elem = (void *)kalloc(size);
-               }
+#pragma unused(oidp, arg1, arg2)
+       /* require setting this sysctl to prevent sysctl -a from running this */
+       if (!req->newptr) {
+               return 0;
+       }
 
-       return (elem);
+       int ret_val = 0;
+       zone_gc_replenish_test();
+       return SYSCTL_OUT(req, &ret_val, sizeof(ret_val));
 }
 
-void _FREE_ZONE(
-       void            *elem,
-       size_t          size,
-       int             type)
+static int
+sysctl_zone_alloc_replenish_test SYSCTL_HANDLER_ARGS
 {
-       struct kmzones  *kmz;
-
-       if (type >= M_LAST)
-               panic("FREE_SIZE");
-
-       kmz = &kmzones[type];
-       if (kmz->kz_zalloczone == KMZ_MALLOC)
-               panic("free_zone ZONE");
-
-/* XXX */
-       if (kmz->kz_elemsize == -1)
-               panic("FREE_SIZE XXX");
-/* XXX */
-       if (size == kmz->kz_elemsize)
-               zfree(kmz->kz_zalloczone, (vm_offset_t)elem);
-       else
-               kfree((vm_offset_t)elem, size);
+#pragma unused(oidp, arg1, arg2)
+       /* require setting this sysctl to prevent sysctl -a from running this */
+       if (!req->newptr) {
+               return 0;
+       }
+
+       int ret_val = 0;
+       zone_alloc_replenish_test();
+       return SYSCTL_OUT(req, &ret_val, sizeof(ret_val));
 }
+
+SYSCTL_PROC(_kern, OID_AUTO, zone_gc_replenish_test,
+    CTLTYPE_INT | CTLFLAG_MASKED | CTLFLAG_LOCKED | CTLFLAG_WR,
+    0, 0, &sysctl_zone_gc_replenish_test, "I", "Test zone GC replenish");
+SYSCTL_PROC(_kern, OID_AUTO, zone_alloc_replenish_test,
+    CTLTYPE_INT | CTLFLAG_MASKED | CTLFLAG_LOCKED | CTLFLAG_WR,
+    0, 0, &sysctl_zone_alloc_replenish_test, "I", "Test zone alloc replenish");
+
+#endif /* DEBUG || DEVELOPMENT */