1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * These routines simply provide wrappers around malloc(3C) and free(3C) 31 * for now. In the future we hope to provide a userland equivalent to 32 * the kmem allocator, including cache allocators. 33 */ 34 35 #include <strings.h> 36 #include <stdlib.h> 37 #include <poll.h> 38 39 #ifdef _KMDB 40 #include <kmdb/kmdb_fault.h> 41 #endif 42 #include <mdb/mdb_debug.h> 43 #include <mdb/mdb_stdlib.h> 44 #include <mdb/mdb_frame.h> 45 #include <mdb/mdb_umem.h> 46 #include <mdb/mdb_err.h> 47 #include <mdb/mdb.h> 48 49 #define UMF_DEBUG 0x1 50 51 #ifdef DEBUG 52 int mdb_umem_flags = UMF_DEBUG; 53 #else 54 int mdb_umem_flags = 0; 55 #endif 56 57 struct mdb_mblk { 58 void *blk_addr; /* address of allocated block */ 59 size_t blk_size; /* size of block in bytes */ 60 struct mdb_mblk *blk_next; /* link to next block */ 61 }; 62 63 /*ARGSUSED*/ 64 static void * 65 mdb_umem_handler(size_t nbytes, size_t align, uint_t flags) 66 { 67 #ifdef _KMDB 68 69 /* 70 * kmdb has a fixed, dedicated VA range in which to play. This range 71 * won't change size while the debugger is running, regardless of how 72 * long we wait. As a result, the only sensible course of action is 73 * to fail the request. If we're here, however, the request was made 74 * with UM_SLEEP. The caller is thus not expecting a NULL back. We'll 75 * have to fail the current dcmd set. 76 */ 77 if (mdb.m_depth > 0) { 78 warn("failed to allocate %lu bytes -- recovering\n", 79 (ulong_t)nbytes); 80 81 kmdb_print_stack(); 82 83 longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM); 84 } 85 86 #else 87 88 /* 89 * mdb, on the other hand, can afford to wait, as someone may actually 90 * free something. 91 */ 92 if (errno == EAGAIN) { 93 void *ptr = NULL; 94 char buf[64]; 95 96 (void) mdb_iob_snprintf(buf, sizeof (buf), 97 "[ sleeping for %lu bytes of free memory ... ]", 98 (ulong_t)nbytes); 99 100 (void) mdb_iob_puts(mdb.m_err, buf); 101 (void) mdb_iob_flush(mdb.m_err); 102 103 do { 104 (void) poll(NULL, 0, 1000); 105 if (align != 0) 106 ptr = memalign(align, nbytes); 107 else 108 ptr = malloc(nbytes); 109 } while (ptr == NULL && errno == EAGAIN); 110 111 if (ptr != NULL) 112 return (ptr); 113 114 (void) memset(buf, '\b', strlen(buf)); 115 (void) mdb_iob_puts(mdb.m_err, buf); 116 (void) mdb_iob_flush(mdb.m_err); 117 118 (void) memset(buf, ' ', strlen(buf)); 119 (void) mdb_iob_puts(mdb.m_err, buf); 120 (void) mdb_iob_flush(mdb.m_err); 121 122 (void) memset(buf, '\b', strlen(buf)); 123 (void) mdb_iob_puts(mdb.m_err, buf); 124 (void) mdb_iob_flush(mdb.m_err); 125 } 126 #endif 127 128 die("failed to allocate %lu bytes -- terminating\n", (ulong_t)nbytes); 129 130 /*NOTREACHED*/ 131 132 return (NULL); 133 } 134 135 static void 136 mdb_umem_gc_enter(void *ptr, size_t nbytes) 137 { 138 mdb_mblk_t *blkp = mdb_alloc(sizeof (mdb_mblk_t), UM_SLEEP); 139 140 blkp->blk_addr = ptr; 141 blkp->blk_size = nbytes; 142 blkp->blk_next = mdb.m_frame->f_mblks; 143 144 mdb.m_frame->f_mblks = blkp; 145 } 146 147 /* 148 * If we're compiled in debug mode, we use this function (gratuitously 149 * stolen from kmem.c) to set uninitialized and freed regions to 150 * special bit patterns. 151 */ 152 static void 153 mdb_umem_copy_pattern(uint32_t pattern, void *buf_arg, size_t size) 154 { 155 /* LINTED - alignment of bufend */ 156 uint32_t *bufend = (uint32_t *)((char *)buf_arg + size); 157 uint32_t *buf = buf_arg; 158 159 while (buf < bufend - 3) { 160 buf[3] = buf[2] = buf[1] = buf[0] = pattern; 161 buf += 4; 162 } 163 164 while (buf < bufend) 165 *buf++ = pattern; 166 } 167 168 void * 169 mdb_alloc_align(size_t nbytes, size_t align, uint_t flags) 170 { 171 void *ptr; 172 173 if (nbytes == 0) 174 return (NULL); 175 176 nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1); 177 178 if (align != 0) 179 ptr = memalign(align, nbytes); 180 else 181 ptr = malloc(nbytes); 182 183 if (flags & UM_SLEEP) { 184 while (ptr == NULL) 185 ptr = mdb_umem_handler(nbytes, align, flags); 186 } 187 188 if (ptr != NULL && (mdb_umem_flags & UMF_DEBUG) != 0) 189 mdb_umem_copy_pattern(UMEM_UNINITIALIZED_PATTERN, ptr, nbytes); 190 191 if (flags & UM_GC) 192 mdb_umem_gc_enter(ptr, nbytes); 193 194 return (ptr); 195 } 196 197 void * 198 mdb_alloc(size_t nbytes, uint_t flags) 199 { 200 return (mdb_alloc_align(nbytes, 0, flags)); 201 } 202 203 void * 204 mdb_zalloc(size_t nbytes, uint_t flags) 205 { 206 void *ptr = mdb_alloc(nbytes, flags); 207 208 if (ptr != NULL) 209 bzero(ptr, nbytes); 210 211 return (ptr); 212 } 213 214 void 215 mdb_free(void *ptr, size_t nbytes) 216 { 217 ASSERT(ptr != NULL || nbytes == 0); 218 219 nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1); 220 221 if (ptr != NULL) { 222 if (mdb_umem_flags & UMF_DEBUG) 223 mdb_umem_copy_pattern(UMEM_FREE_PATTERN, ptr, nbytes); 224 free(ptr); 225 } 226 } 227 228 void 229 mdb_free_align(void *ptr, size_t nbytes) 230 { 231 mdb_free(ptr, nbytes); 232 } 233 234 void 235 mdb_recycle(mdb_mblk_t **blkpp) 236 { 237 mdb_mblk_t *blkp, *nblkp; 238 239 for (blkp = *blkpp; blkp != NULL; blkp = nblkp) { 240 mdb_dprintf(MDB_DBG_UMEM, 241 "garbage collect %p size %lu bytes\n", blkp->blk_addr, 242 (ulong_t)blkp->blk_size); 243 244 nblkp = blkp->blk_next; 245 mdb_free(blkp->blk_addr, blkp->blk_size); 246 mdb_free(blkp, sizeof (mdb_mblk_t)); 247 } 248 249 *blkpp = NULL; 250 } 251