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 *
mdb_umem_handler(size_t nbytes,size_t align,uint_t flags)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
mdb_umem_gc_enter(void * ptr,size_t nbytes)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
mdb_umem_copy_pattern(uint32_t pattern,void * buf_arg,size_t size)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 *
mdb_alloc_align(size_t nbytes,size_t align,uint_t flags)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 *
mdb_alloc(size_t nbytes,uint_t flags)198 mdb_alloc(size_t nbytes, uint_t flags)
199 {
200 return (mdb_alloc_align(nbytes, 0, flags));
201 }
202
203 void *
mdb_zalloc(size_t nbytes,uint_t flags)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
mdb_free(void * ptr,size_t nbytes)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
mdb_free_align(void * ptr,size_t nbytes)229 mdb_free_align(void *ptr, size_t nbytes)
230 {
231 mdb_free(ptr, nbytes);
232 }
233
234 void
mdb_recycle(mdb_mblk_t ** blkpp)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