]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_malloc.c
xnu-1699.32.7.tar.gz
[apple/xnu.git] / bsd / kern / kern_malloc.c
index ff86bfff64b20062ca3ec07ab8592cc67b96ae11..c1700ee51a7e6c5f8dfbdf2db15021932b49fb55 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -293,9 +293,15 @@ const char *memname[] = {
 #else
        "",             /* 109 M_DECMPFS_CNODE */
 #endif /* HFS_COMPRESSION */
+       "ipmfilter",    /* 110 M_INMFILTER */
+       "ipmsource",    /* 111 M_IPMSOURCE */
+       "in6mfilter",   /* 112 M_IN6MFILTER */
+       "ip6mopts",     /* 113 M_IP6MOPTS */
+       "ip6msource",   /* 114 M_IP6MSOURCE */
 };
 
 /* for use with kmzones.kz_zalloczone */
+#define KMZ_CREATEZONE_ACCT    ((void *)-3)
 #define        KMZ_CREATEZONE          ((void *)-2)
 #define KMZ_LOOKUPZONE         ((void *)-1)
 #define KMZ_MALLOC                     ((void *)0)
@@ -332,7 +338,7 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 21 M_FHANDLE */
 #if (NFSCLIENT || NFSSERVER)
        { SOS(nfsreq),  KMZ_CREATEZONE, FALSE },        /* 22 M_NFSREQ */
-       { SOS(nfsmount),        KMZ_CREATEZONE, FALSE },/* 23 M_NFSMNT */
+       { SOS(nfsmount),KMZ_CREATEZONE, FALSE },        /* 23 M_NFSMNT */
        { SOS(nfsnode), KMZ_CREATEZONE, FALSE },        /* 24 M_NFSNODE */
 #else
        { 0,            KMZ_MALLOC, FALSE },            /* 22 M_NFSREQ */
@@ -340,25 +346,25 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 24 M_NFSNODE */
 #endif
        { SOS(vnode),   KMZ_CREATEZONE, TRUE },         /* 25 M_VNODE */
-       { SOS(namecache),KMZ_CREATEZONE, FALSE },       /* 26 M_CACHE */
+       { SOS(namecache), KMZ_CREATEZONE, FALSE },      /* 26 M_CACHE */
 #if QUOTA
        { SOX(dquot),   KMZ_LOOKUPZONE, FALSE },        /* 27 M_DQUOT */
 #else
        { 0,            KMZ_MALLOC, FALSE },            /* 27 M_DQUOT */
 #endif
        { 0,            KMZ_MALLOC, FALSE },            /* 28 M_UFSMNT */
-       { 0,            KMZ_MALLOC, FALSE },            /* 29 M_CGSUM */
+       { 0,            KMZ_MALLOC, FALSE },            /* 29 M_SHM */
        { SOS(plimit),  KMZ_CREATEZONE, TRUE },         /* 30 M_PLIMIT */
-       { SOS(sigacts), KMZ_CREATEZONE, TRUE },         /* 31 M_SIGACTS */
+       { SOS(sigacts), KMZ_CREATEZONE_ACCT, TRUE },    /* 31 M_SIGACTS */
        { 0,            KMZ_MALLOC, FALSE },            /* 32 M_VMOBJ */
        { 0,            KMZ_MALLOC, FALSE },            /* 33 M_VMOBJHASH */
        { 0,            KMZ_MALLOC, FALSE },            /* 34 M_VMPMAP */
        { 0,            KMZ_MALLOC, FALSE },            /* 35 M_VMPVENT */
        { 0,            KMZ_MALLOC, FALSE },            /* 36 M_VMPAGER */
        { 0,            KMZ_MALLOC, FALSE },            /* 37 M_VMPGDATA */
-       { SOS(fileproc),KMZ_CREATEZONE, TRUE },         /* 38 M_FILEPROC */
-       { SOS(filedesc),KMZ_CREATEZONE, TRUE },         /* 39 M_FILEDESC */
-       { SOX(lockf),   KMZ_CREATEZONE, TRUE },         /* 40 M_LOCKF */
+       { SOS(fileproc),KMZ_CREATEZONE_ACCT, TRUE },    /* 38 M_FILEPROC */
+       { SOS(filedesc),KMZ_CREATEZONE_ACCT, TRUE },    /* 39 M_FILEDESC */
+       { SOX(lockf),   KMZ_CREATEZONE_ACCT, TRUE },    /* 40 M_LOCKF */
        { SOS(proc),    KMZ_CREATEZONE, FALSE },        /* 41 M_PROC */
        { SOS(pstats),  KMZ_CREATEZONE, TRUE },         /* 42 M_PSTATS */
        { 0,            KMZ_MALLOC, FALSE },            /* 43 M_SEGMENT */
@@ -370,10 +376,10 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 49 M_NETADDR */
 #if (NFSCLIENT || NFSSERVER)
        { SOX(nfsrv_sock),
-                       KMZ_CREATEZONE, FALSE },        /* 50 M_NFSSVC */
+                       KMZ_CREATEZONE_ACCT, FALSE },   /* 50 M_NFSSVC */
        { 0,            KMZ_MALLOC, FALSE },            /* 51 M_NFSUID */
        { SOX(nfsrvcache),
-                       KMZ_CREATEZONE, FALSE },        /* 52 M_NFSD */
+                       KMZ_CREATEZONE_ACCT, FALSE },   /* 52 M_NFSD */
 #else
        { 0,            KMZ_MALLOC, FALSE },            /* 50 M_NFSSVC */
        { 0,            KMZ_MALLOC, FALSE },            /* 51 M_NFSUID */
@@ -389,7 +395,7 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 58 unused entry */
 #if (NFSCLIENT || NFSSERVER)
        { SOS(nfsrv_descript),
-                       KMZ_CREATEZONE, FALSE },        /* 59 M_NFSRVDESC */
+                       KMZ_CREATEZONE_ACCT, FALSE },   /* 59 M_NFSRVDESC */
        { SOS(nfsdmap), KMZ_CREATEZONE, FALSE },        /* 60 M_NFSDIROFF */
        { SOS(fhandle), KMZ_LOOKUPZONE, FALSE },        /* 61 M_NFSBIGFH */
 #else
@@ -407,9 +413,9 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 69 M_ADOSFSMNT */
        { 0,            KMZ_MALLOC, FALSE },            /* 70 M_ADOSFSNODE */
        { 0,            KMZ_MALLOC, FALSE },            /* 71 M_ANODE */
-       { SOX(buf),     KMZ_CREATEZONE, TRUE },         /* 72 M_BUFHDR */
+       { 0,            KMZ_MALLOC, TRUE },             /* 72 M_BUFHDR */
        { (NDFILE * OFILESIZE),
-                       KMZ_CREATEZONE, FALSE },        /* 73 M_OFILETABL */
+                       KMZ_CREATEZONE_ACCT, FALSE },   /* 73 M_OFILETABL */
        { MCLBYTES,     KMZ_CREATEZONE, FALSE },        /* 74 M_MCLUST */
 #if HFS
        { SOX(hfsmount),KMZ_LOOKUPZONE, FALSE },        /* 75 M_HFSMNT */
@@ -437,15 +443,15 @@ struct kmzones {
        { SOS(journal), KMZ_CREATEZONE, FALSE },        /* 91 M_JNL_JNL */
        { SOS(transaction), KMZ_CREATEZONE, FALSE },    /* 92 M_JNL_TR */
 #else
-       { 0,     KMZ_MALLOC, FALSE },                   /* 91 M_JNL_JNL */
-       { 0,     KMZ_MALLOC, FALSE },                   /* 92 M_JNL_TR */
+       { 0,            KMZ_MALLOC, FALSE },            /* 91 M_JNL_JNL */
+       { 0,            KMZ_MALLOC, FALSE },            /* 92 M_JNL_TR */
 #endif
-       { SOS(specinfo), KMZ_CREATEZONE, TRUE },        /* 93 M_SPECINFO */
-       { SOS(kqueue), KMZ_CREATEZONE, FALSE },         /* 94 M_KQUEUE */
+       { SOS(specinfo),KMZ_CREATEZONE, TRUE },         /* 93 M_SPECINFO */
+       { SOS(kqueue),  KMZ_CREATEZONE, FALSE },        /* 94 M_KQUEUE */
 #if HFS
-       { SOS(directoryhint), KMZ_CREATEZONE, FALSE },  /* 95 M_HFSDIRHINT */
+       { SOS(directoryhint), KMZ_CREATEZONE, TRUE },   /* 95 M_HFSDIRHINT */
 #else
-       { 0,    KMZ_MALLOC, FALSE },                    /* 95 M_HFSDIRHINT */
+       { 0,            KMZ_MALLOC, FALSE },            /* 95 M_HFSDIRHINT */
 #endif
        { SOS(cl_readahead),  KMZ_CREATEZONE, TRUE },   /* 96 M_CLRDAHEAD */
        { SOS(cl_writebehind),KMZ_CREATEZONE, TRUE },   /* 97 M_CLWRBEHIND */
@@ -454,7 +460,7 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 100 M_KAUTH */
        { 0,            KMZ_MALLOC, FALSE },            /* 101 M_DUMMYNET */
 #ifndef __LP64__
-       { SOS(unsafe_fsnode),KMZ_CREATEZONE, FALSE },   /* 102 M_UNSAFEFS */
+       { SOS(unsafe_fsnode),KMZ_CREATEZONE, TRUE },    /* 102 M_UNSAFEFS */
 #else 
        { 0,            KMZ_MALLOC, FALSE },            /* 102 M_UNSAFEFS */
 #endif /* __LP64__ */
@@ -465,10 +471,15 @@ struct kmzones {
        { 0,            KMZ_MALLOC, FALSE },            /* 107 M_LCTX */
        { 0,            KMZ_MALLOC, FALSE },            /* 108 M_TRAFFIC_MGT */
 #if HFS_COMPRESSION
-       { SOS(decmpfs_cnode),KMZ_CREATEZONE, FALSE },   /* 109 M_DECMPFS_CNODE */
+       { SOS(decmpfs_cnode),KMZ_CREATEZONE , FALSE},   /* 109 M_DECMPFS_CNODE */
 #else
        { 0,            KMZ_MALLOC, FALSE },            /* 109 M_DECMPFS_CNODE */
 #endif /* HFS_COMPRESSION */
+       { 0,            KMZ_MALLOC, FALSE },            /* 110 M_INMFILTER */
+       { 0,            KMZ_MALLOC, FALSE },            /* 111 M_IPMSOURCE */
+       { 0,            KMZ_MALLOC, FALSE },            /* 112 M_IN6MFILTER */
+       { 0,            KMZ_MALLOC, FALSE },            /* 113 M_IP6MOPTS */
+       { 0,            KMZ_MALLOC, FALSE },            /* 114 M_IP6MSOURCE */
 #undef SOS
 #undef SOX
 };
@@ -495,10 +506,14 @@ kmeminit(void)
                        ;
                else
 /* XXX */
-               if (kmz->kz_zalloczone == KMZ_CREATEZONE) {
+               if (kmz->kz_zalloczone == KMZ_CREATEZONE ||
+                   kmz->kz_zalloczone == KMZ_CREATEZONE_ACCT) {
                        kmz->kz_zalloczone = zinit(kmz->kz_elemsize,
                                                1024 * 1024, PAGE_SIZE,
                                                memname[kmz - kmzones]);
+                       zone_change(kmz->kz_zalloczone, Z_CALLERACCT,
+                                   (kmz->kz_zalloczone == KMZ_CREATEZONE_ACCT));
+
                        if (kmz->kz_noencrypt == TRUE)
                                zone_change(kmz->kz_zalloczone, Z_NOENCRYPT, TRUE);
                }
@@ -526,12 +541,6 @@ kmeminit(void)
        }
 }
 
-#define        MDECL(reqlen)                                   \
-union {                                                        \
-       struct  _mhead hdr;                             \
-       char    _m[(reqlen) + sizeof (struct _mhead)];  \
-}
-
 struct _mhead {
        size_t  mlen;
        char    dat[0];
@@ -543,8 +552,8 @@ _MALLOC(
        int             type,
        int             flags)
 {
-       MDECL(size)     *mem;
-       size_t          memsize = sizeof (*mem);
+       struct _mhead   *hdr;
+       size_t          memsize = sizeof (*hdr) + size;
 
        if (type >= M_LAST)
                panic("_malloc TYPE");
@@ -553,11 +562,11 @@ _MALLOC(
                return (NULL);
 
        if (flags & M_NOWAIT) {
-               mem = (void *)kalloc_noblock(memsize);
+               hdr = (void *)kalloc_noblock(memsize);
        } else {
-               mem = (void *)kalloc(memsize);
+               hdr = (void *)kalloc(memsize);
 
-               if (mem == NULL) {
+               if (hdr == NULL) {
 
                        /*
                         * We get here when the caller told us to block waiting for memory, but
@@ -572,15 +581,15 @@ _MALLOC(
                        panic("_MALLOC: kalloc returned NULL (potential leak), size %llu", (uint64_t) size);
                }
        }
-       if (!mem)
+       if (!hdr)
                return (0);
 
-       mem->hdr.mlen = memsize;
+       hdr->mlen = memsize;
 
        if (flags & M_ZERO)
-               bzero(mem->hdr.dat, size);
+               bzero(hdr->dat, size);
 
-       return  (mem->hdr.dat);
+       return  (hdr->dat);
 }
 
 void
@@ -600,6 +609,36 @@ _FREE(
        kfree(hdr, hdr->mlen);
 }
 
+void *
+_REALLOC(
+       void            *addr,
+       size_t          size,
+       int             type,
+       int             flags)
+{
+       struct _mhead   *hdr;
+       void            *newaddr;
+       size_t          alloc;
+
+       /* realloc(NULL, ...) is equivalent to malloc(...) */
+       if (addr == NULL)
+               return (_MALLOC(size, type, flags));
+
+       /* Allocate a new, bigger (or smaller) block */
+       if ((newaddr = _MALLOC(size, type, flags)) == NULL)
+               return (NULL);
+
+       hdr = addr;
+       --hdr;
+       alloc = hdr->mlen - sizeof (*hdr);
+
+       /* Copy over original contents */
+       bcopy(addr, newaddr, MIN(size, alloc));
+       _FREE(addr, type);
+
+       return (newaddr);
+}
+
 void *
 _MALLOC_ZONE(
        size_t          size,
@@ -660,3 +699,116 @@ _FREE_ZONE(
        else
                kfree(elem, size);
 }
+
+#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);
+       /*
+        * Can only be activated if it's off (and not failed.)
+        * Cannot be deactivated once it's on.
+        */
+       if (val == 1 && oldval == 0) {
+               kern_return_t kr = zleak_activate();
+
+               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);
+       }
+       return (0);
+}
+
+SYSCTL_PROC(_kern_zleak, OID_AUTO, active,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+    0, 0, sysctl_zleak_active, "I", "zleak activity");
+
+/*
+ * 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);
+}
+
+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
+{
+#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 (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");
+
+#endif /* CONFIG_ZLEAKS */