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